blob: 30df6ade05100f23fd0c6904713fd47ff19efe63 [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"
henrike@webrtc.org9de257d2013-07-17 14:42:53 +000075#include "webrtc/system_wrappers/interface/trace.h"
76#include "webrtc/video_engine/include/vie_base.h"
77#include "webrtc/voice_engine/include/voe_base.h"
henrike@webrtc.org28e20752013-07-10 00:45:36 +000078
fischman@webrtc.org7e4d0df2013-10-01 02:40:43 +000079#ifdef ANDROID
80#include "webrtc/system_wrappers/interface/logcat_trace_context.h"
81using webrtc::LogcatTraceContext;
82#endif
83
henrike@webrtc.org28e20752013-07-10 00:45:36 +000084using icu::UnicodeString;
85using webrtc::AudioSourceInterface;
86using webrtc::AudioTrackInterface;
87using webrtc::AudioTrackVector;
88using webrtc::CreateSessionDescriptionObserver;
henrike@webrtc.org723d6832013-07-12 16:04:50 +000089using webrtc::DataBuffer;
90using webrtc::DataChannelInit;
91using webrtc::DataChannelInterface;
92using webrtc::DataChannelObserver;
henrike@webrtc.org28e20752013-07-10 00:45:36 +000093using webrtc::IceCandidateInterface;
94using webrtc::MediaConstraintsInterface;
95using webrtc::MediaSourceInterface;
96using webrtc::MediaStreamInterface;
97using webrtc::MediaStreamTrackInterface;
98using webrtc::PeerConnectionFactoryInterface;
99using webrtc::PeerConnectionInterface;
100using webrtc::PeerConnectionObserver;
101using webrtc::SessionDescriptionInterface;
102using webrtc::SetSessionDescriptionObserver;
103using webrtc::StatsObserver;
104using webrtc::StatsReport;
105using webrtc::VideoRendererInterface;
106using webrtc::VideoSourceInterface;
107using webrtc::VideoTrackInterface;
108using webrtc::VideoTrackVector;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000109
110// Abort the process if |x| is false, emitting |msg|.
111#define CHECK(x, msg) \
112 if (x) {} else { \
113 LOG(LS_ERROR) << __FILE__ << ":" << __LINE__ << ": " << msg; \
114 abort(); \
115 }
116// Abort the process if |jni| has a Java exception pending, emitting |msg|.
117#define CHECK_EXCEPTION(jni, msg) \
118 if (0) {} else { \
119 if (jni->ExceptionCheck()) { \
120 jni->ExceptionDescribe(); \
121 jni->ExceptionClear(); \
122 CHECK(0, msg); \
123 } \
124 }
125
fischman@webrtc.org32001ef2013-08-12 23:26:21 +0000126// Helper that calls ptr->Release() and logs a useful message if that didn't
127// actually delete *ptr because of extra refcounts.
128#define CHECK_RELEASE(ptr) \
129 do { \
130 int count = (ptr)->Release(); \
131 if (count != 0) { \
132 LOG(LS_ERROR) << "Refcount unexpectedly not 0: " << (ptr) \
133 << ": " << count; \
134 } \
135 CHECK(!count, "Unexpected refcount"); \
136 } while (0)
137
fischman@webrtc.org87881672013-09-03 18:58:12 +0000138// Lifted from chromium's base/basictypes.h.
139template <bool>
140struct CompileAssert {};
141#define COMPILE_ASSERT(expr, msg) \
142 typedef CompileAssert<(bool(expr))> msg[bool(expr) ? 1 : -1]
143
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000144namespace {
145
146static JavaVM* g_jvm = NULL; // Set in JNI_OnLoad().
147
148static pthread_once_t g_jni_ptr_once = PTHREAD_ONCE_INIT;
149static pthread_key_t g_jni_ptr; // Key for per-thread JNIEnv* data.
150
fischman@webrtc.org32001ef2013-08-12 23:26:21 +0000151// Return thread ID as a string.
152static std::string GetThreadId() {
153 char buf[21]; // Big enough to hold a kuint64max plus terminating NULL.
154 CHECK(snprintf(buf, sizeof(buf), "%llu", syscall(__NR_gettid)) <= sizeof(buf),
155 "Thread id is bigger than uint64??");
156 return std::string(buf);
157}
158
159// Return the current thread's name.
160static std::string GetThreadName() {
161 char name[17];
162 CHECK(prctl(PR_GET_NAME, name) == 0, "prctl(PR_GET_NAME) failed");
163 name[16] = '\0';
164 return std::string(name);
165}
166
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000167static void ThreadDestructor(void* unused) {
168 jint status = g_jvm->DetachCurrentThread();
169 CHECK(status == JNI_OK, "Failed to detach thread: " << status);
170}
171
172static void CreateJNIPtrKey() {
173 CHECK(!pthread_key_create(&g_jni_ptr, &ThreadDestructor),
174 "pthread_key_create");
175}
176
177// Deal with difference in signatures between Oracle's jni.h and Android's.
178static JNIEnv* AttachCurrentThreadIfNeeded() {
179 CHECK(!pthread_once(&g_jni_ptr_once, &CreateJNIPtrKey),
180 "pthread_once");
181 JNIEnv* jni = reinterpret_cast<JNIEnv*>(pthread_getspecific(g_jni_ptr));
182 if (jni == NULL) {
183#ifdef _JAVASOFT_JNI_H_ // Oracle's jni.h violates the JNI spec!
184 void* env;
185#else
186 JNIEnv* env;
187#endif
fischman@webrtc.org32001ef2013-08-12 23:26:21 +0000188 char* name = strdup((GetThreadName() + " - " + GetThreadId()).c_str());
189 JavaVMAttachArgs args;
190 args.version = JNI_VERSION_1_6;
191 args.name = name;
192 args.group = NULL;
193 CHECK(!g_jvm->AttachCurrentThread(&env, &args), "Failed to attach thread");
194 free(name);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000195 CHECK(env, "AttachCurrentThread handed back NULL!");
196 jni = reinterpret_cast<JNIEnv*>(env);
197 CHECK(!pthread_setspecific(g_jni_ptr, jni), "pthread_setspecific");
198 }
199 return jni;
200}
201
fischman@webrtc.org87881672013-09-03 18:58:12 +0000202// Return a |jlong| that will automatically convert back to |ptr| when assigned
203// to a |uint64|
204static jlong jlongFromPointer(void* ptr) {
205 COMPILE_ASSERT(sizeof(intptr_t) <= sizeof(uint64),
206 Time_to_rethink_the_use_of_jlongs);
207 // Guaranteed to fit by the COMPILE_ASSERT above.
208 uint64 u64 = reinterpret_cast<intptr_t>(ptr);
209 // If the unsigned value fits in the signed type, return it directly.
210 if (u64 <= std::numeric_limits<int64>::max())
211 return u64;
212 // Otherwise, we need to get move u64 into the range of [int64min, -1] subject
213 // to the constraints of remaining equal to |u64| modulo |2^64|.
214 u64 = std::numeric_limits<uint64>::max() - u64; // In [0,int64max].
215 int64 i64 = -u64; // In [-int64max, 0].
216 i64 -= 1; // In [int64min, -1], and i64+2^64==u64.
217 return i64;
218}
219
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000220// Android's FindClass() is trickier than usual because the app-specific
221// ClassLoader is not consulted when there is no app-specific frame on the
222// stack. Consequently, we only look up classes once in JNI_OnLoad.
223// http://developer.android.com/training/articles/perf-jni.html#faq_FindClass
224class ClassReferenceHolder {
225 public:
226 explicit ClassReferenceHolder(JNIEnv* jni) {
227 LoadClass(jni, "java/nio/ByteBuffer");
228 LoadClass(jni, "org/webrtc/AudioTrack");
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000229 LoadClass(jni, "org/webrtc/DataChannel");
230 LoadClass(jni, "org/webrtc/DataChannel$Buffer");
231 LoadClass(jni, "org/webrtc/DataChannel$Init");
232 LoadClass(jni, "org/webrtc/DataChannel$State");
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000233 LoadClass(jni, "org/webrtc/IceCandidate");
234 LoadClass(jni, "org/webrtc/MediaSource$State");
235 LoadClass(jni, "org/webrtc/MediaStream");
236 LoadClass(jni, "org/webrtc/MediaStreamTrack$State");
237 LoadClass(jni, "org/webrtc/PeerConnection$SignalingState");
238 LoadClass(jni, "org/webrtc/PeerConnection$IceConnectionState");
239 LoadClass(jni, "org/webrtc/PeerConnection$IceGatheringState");
240 LoadClass(jni, "org/webrtc/SessionDescription");
241 LoadClass(jni, "org/webrtc/SessionDescription$Type");
242 LoadClass(jni, "org/webrtc/StatsReport");
243 LoadClass(jni, "org/webrtc/StatsReport$Value");
244 LoadClass(jni, "org/webrtc/VideoRenderer$I420Frame");
245 LoadClass(jni, "org/webrtc/VideoTrack");
246 }
247
248 ~ClassReferenceHolder() {
249 CHECK(classes_.empty(), "Must call FreeReferences() before dtor!");
250 }
251
252 void FreeReferences(JNIEnv* jni) {
253 for (std::map<std::string, jclass>::const_iterator it = classes_.begin();
254 it != classes_.end(); ++it) {
255 jni->DeleteGlobalRef(it->second);
256 }
257 classes_.clear();
258 }
259
260 jclass GetClass(const std::string& name) {
261 std::map<std::string, jclass>::iterator it = classes_.find(name);
262 CHECK(it != classes_.end(), "Unexpected GetClass() call for: " << name);
263 return it->second;
264 }
265
266 private:
267 void LoadClass(JNIEnv* jni, const std::string& name) {
268 jclass localRef = jni->FindClass(name.c_str());
269 CHECK_EXCEPTION(jni, "error during FindClass: " << name);
270 CHECK(localRef, name);
271 jclass globalRef = reinterpret_cast<jclass>(jni->NewGlobalRef(localRef));
272 CHECK_EXCEPTION(jni, "error during NewGlobalRef: " << name);
273 CHECK(globalRef, name);
274 bool inserted = classes_.insert(std::make_pair(name, globalRef)).second;
275 CHECK(inserted, "Duplicate class name: " << name);
276 }
277
278 std::map<std::string, jclass> classes_;
279};
280
281// Allocated in JNI_OnLoad(), freed in JNI_OnUnLoad().
282static ClassReferenceHolder* g_class_reference_holder = NULL;
283
284// JNIEnv-helper methods that CHECK success: no Java exception thrown and found
285// object/class/method/field is non-null.
286jmethodID GetMethodID(
287 JNIEnv* jni, jclass c, const std::string& name, const char* signature) {
288 jmethodID m = jni->GetMethodID(c, name.c_str(), signature);
289 CHECK_EXCEPTION(jni,
290 "error during GetMethodID: " << name << ", " << signature);
291 CHECK(m, name << ", " << signature);
292 return m;
293}
294
295jmethodID GetStaticMethodID(
296 JNIEnv* jni, jclass c, const char* name, const char* signature) {
297 jmethodID m = jni->GetStaticMethodID(c, name, signature);
298 CHECK_EXCEPTION(jni,
299 "error during GetStaticMethodID: "
300 << name << ", " << signature);
301 CHECK(m, name << ", " << signature);
302 return m;
303}
304
305jfieldID GetFieldID(
306 JNIEnv* jni, jclass c, const char* name, const char* signature) {
307 jfieldID f = jni->GetFieldID(c, name, signature);
308 CHECK_EXCEPTION(jni, "error during GetFieldID");
309 CHECK(f, name << ", " << signature);
310 return f;
311}
312
313jclass FindClass(JNIEnv* jni, const char* name) {
314 return g_class_reference_holder->GetClass(name);
315}
316
317jclass GetObjectClass(JNIEnv* jni, jobject object) {
318 jclass c = jni->GetObjectClass(object);
319 CHECK_EXCEPTION(jni, "error during GetObjectClass");
320 CHECK(c, "");
321 return c;
322}
323
324jobject GetObjectField(JNIEnv* jni, jobject object, jfieldID id) {
325 jobject o = jni->GetObjectField(object, id);
326 CHECK_EXCEPTION(jni, "error during GetObjectField");
327 CHECK(o, "");
328 return o;
329}
330
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000331jstring GetStringField(JNIEnv* jni, jobject object, jfieldID id) {
332 return static_cast<jstring>(GetObjectField(jni, object, id));
333}
334
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000335jlong GetLongField(JNIEnv* jni, jobject object, jfieldID id) {
336 jlong l = jni->GetLongField(object, id);
337 CHECK_EXCEPTION(jni, "error during GetLongField");
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000338 return l;
339}
340
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000341jint GetIntField(JNIEnv* jni, jobject object, jfieldID id) {
342 jint i = jni->GetIntField(object, id);
343 CHECK_EXCEPTION(jni, "error during GetIntField");
344 return i;
345}
346
347bool GetBooleanField(JNIEnv* jni, jobject object, jfieldID id) {
348 jboolean b = jni->GetBooleanField(object, id);
349 CHECK_EXCEPTION(jni, "error during GetBooleanField");
350 return b;
351}
352
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000353jobject NewGlobalRef(JNIEnv* jni, jobject o) {
354 jobject ret = jni->NewGlobalRef(o);
355 CHECK_EXCEPTION(jni, "error during NewGlobalRef");
356 CHECK(ret, "");
357 return ret;
358}
359
360void DeleteGlobalRef(JNIEnv* jni, jobject o) {
361 jni->DeleteGlobalRef(o);
362 CHECK_EXCEPTION(jni, "error during DeleteGlobalRef");
363}
364
365// Given a jweak reference, allocate a (strong) local reference scoped to the
366// lifetime of this object if the weak reference is still valid, or NULL
367// otherwise.
368class WeakRef {
369 public:
370 WeakRef(JNIEnv* jni, jweak ref)
371 : jni_(jni), obj_(jni_->NewLocalRef(ref)) {
372 CHECK_EXCEPTION(jni, "error during NewLocalRef");
373 }
374 ~WeakRef() {
375 if (obj_) {
376 jni_->DeleteLocalRef(obj_);
377 CHECK_EXCEPTION(jni_, "error during DeleteLocalRef");
378 }
379 }
380 jobject obj() { return obj_; }
381
382 private:
383 JNIEnv* const jni_;
384 jobject const obj_;
385};
386
387// Given a local ref, take ownership of it and delete the ref when this goes out
388// of scope.
389template<class T> // T is jclass, jobject, jintArray, etc.
390class ScopedLocalRef {
391 public:
392 ScopedLocalRef(JNIEnv* jni, T obj)
393 : jni_(jni), obj_(obj) {}
394 ~ScopedLocalRef() {
395 jni_->DeleteLocalRef(obj_);
396 }
397 T operator*() const {
398 return obj_;
399 }
400 private:
401 JNIEnv* jni_;
402 T obj_;
403};
404
405// Scoped holder for global Java refs.
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000406template<class T> // T is jclass, jobject, jintArray, etc.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000407class ScopedGlobalRef {
408 public:
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000409 explicit ScopedGlobalRef(JNIEnv* jni, T obj)
410 : obj_(static_cast<T>(jni->NewGlobalRef(obj))) {}
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000411 ~ScopedGlobalRef() {
412 DeleteGlobalRef(AttachCurrentThreadIfNeeded(), obj_);
413 }
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000414 T operator*() const {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000415 return obj_;
416 }
417 private:
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000418 T obj_;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000419};
420
421// Return the (singleton) Java Enum object corresponding to |index|;
422// |state_class_fragment| is something like "MediaSource$State".
423jobject JavaEnumFromIndex(
424 JNIEnv* jni, const std::string& state_class_fragment, int index) {
425 std::string state_class_name = "org/webrtc/" + state_class_fragment;
426 jclass state_class = FindClass(jni, state_class_name.c_str());
427 jmethodID state_values_id = GetStaticMethodID(
428 jni, state_class, "values", ("()[L" + state_class_name + ";").c_str());
429 ScopedLocalRef<jobjectArray> state_values(
430 jni,
431 (jobjectArray)jni->CallStaticObjectMethod(state_class, state_values_id));
432 CHECK_EXCEPTION(jni, "error during CallStaticObjectMethod");
433 jobject ret = jni->GetObjectArrayElement(*state_values, index);
434 CHECK_EXCEPTION(jni, "error during GetObjectArrayElement");
435 return ret;
436}
437
438// Given a UTF-8 encoded |native| string return a new (UTF-16) jstring.
439static jstring JavaStringFromStdString(JNIEnv* jni, const std::string& native) {
440 UnicodeString ustr(UnicodeString::fromUTF8(native));
441 jstring jstr = jni->NewString(ustr.getBuffer(), ustr.length());
442 CHECK_EXCEPTION(jni, "error during NewString");
443 return jstr;
444}
445
446// Given a (UTF-16) jstring return a new UTF-8 native string.
447static std::string JavaToStdString(JNIEnv* jni, const jstring& j_string) {
448 const jchar* jchars = jni->GetStringChars(j_string, NULL);
449 CHECK_EXCEPTION(jni, "Error during GetStringChars");
450 UnicodeString ustr(jchars, jni->GetStringLength(j_string));
451 CHECK_EXCEPTION(jni, "Error during GetStringLength");
452 jni->ReleaseStringChars(j_string, jchars);
453 CHECK_EXCEPTION(jni, "Error during ReleaseStringChars");
454 std::string ret;
455 return ustr.toUTF8String(ret);
456}
457
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000458static DataChannelInit JavaDataChannelInitToNative(
459 JNIEnv* jni, jobject j_init) {
460 DataChannelInit init;
461
462 jclass j_init_class = FindClass(jni, "org/webrtc/DataChannel$Init");
463 jfieldID ordered_id = GetFieldID(jni, j_init_class, "ordered", "Z");
464 jfieldID max_retransmit_time_id =
465 GetFieldID(jni, j_init_class, "maxRetransmitTimeMs", "I");
466 jfieldID max_retransmits_id =
467 GetFieldID(jni, j_init_class, "maxRetransmits", "I");
468 jfieldID protocol_id =
469 GetFieldID(jni, j_init_class, "protocol", "Ljava/lang/String;");
470 jfieldID negotiated_id = GetFieldID(jni, j_init_class, "negotiated", "Z");
471 jfieldID id_id = GetFieldID(jni, j_init_class, "id", "I");
472
473 init.ordered = GetBooleanField(jni, j_init, ordered_id);
474 init.maxRetransmitTime = GetIntField(jni, j_init, max_retransmit_time_id);
475 init.maxRetransmits = GetIntField(jni, j_init, max_retransmits_id);
476 init.protocol = JavaToStdString(
477 jni, GetStringField(jni, j_init, protocol_id));
478 init.negotiated = GetBooleanField(jni, j_init, negotiated_id);
479 init.id = GetIntField(jni, j_init, id_id);
480
481 return init;
482}
483
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000484class ConstraintsWrapper;
485
486// Adapter between the C++ PeerConnectionObserver interface and the Java
487// PeerConnection.Observer interface. Wraps an instance of the Java interface
488// and dispatches C++ callbacks to Java.
489class PCOJava : public PeerConnectionObserver {
490 public:
491 PCOJava(JNIEnv* jni, jobject j_observer)
492 : j_observer_global_(jni, j_observer),
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000493 j_observer_class_(jni, GetObjectClass(jni, *j_observer_global_)),
494 j_media_stream_class_(jni, FindClass(jni, "org/webrtc/MediaStream")),
495 j_media_stream_ctor_(GetMethodID(
496 jni, *j_media_stream_class_, "<init>", "(J)V")),
497 j_audio_track_class_(jni, FindClass(jni, "org/webrtc/AudioTrack")),
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000498 j_audio_track_ctor_(GetMethodID(
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000499 jni, *j_audio_track_class_, "<init>", "(J)V")),
500 j_video_track_class_(jni, FindClass(jni, "org/webrtc/VideoTrack")),
501 j_video_track_ctor_(GetMethodID(
502 jni, *j_video_track_class_, "<init>", "(J)V")),
503 j_data_channel_class_(jni, FindClass(jni, "org/webrtc/DataChannel")),
504 j_data_channel_ctor_(GetMethodID(
505 jni, *j_data_channel_class_, "<init>", "(J)V")) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000506 }
507
508 virtual ~PCOJava() {}
509
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000510 virtual void OnIceCandidate(const IceCandidateInterface* candidate) OVERRIDE {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000511 std::string sdp;
512 CHECK(candidate->ToString(&sdp), "got so far: " << sdp);
513 jclass candidate_class = FindClass(jni(), "org/webrtc/IceCandidate");
514 jmethodID ctor = GetMethodID(jni(), candidate_class,
515 "<init>", "(Ljava/lang/String;ILjava/lang/String;)V");
516 ScopedLocalRef<jstring> j_mid(
517 jni(), JavaStringFromStdString(jni(), candidate->sdp_mid()));
518 ScopedLocalRef<jstring> j_sdp(jni(), JavaStringFromStdString(jni(), sdp));
519 ScopedLocalRef<jobject> j_candidate(jni(), jni()->NewObject(
520 candidate_class, ctor, *j_mid, candidate->sdp_mline_index(), *j_sdp));
521 CHECK_EXCEPTION(jni(), "error during NewObject");
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000522 jmethodID m = GetMethodID(jni(), *j_observer_class_,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000523 "onIceCandidate", "(Lorg/webrtc/IceCandidate;)V");
524 jni()->CallVoidMethod(*j_observer_global_, m, *j_candidate);
525 CHECK_EXCEPTION(jni(), "error during CallVoidMethod");
526 }
527
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000528 virtual void OnError() OVERRIDE {
529 jmethodID m = GetMethodID(jni(), *j_observer_class_, "onError", "(V)V");
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000530 jni()->CallVoidMethod(*j_observer_global_, m);
531 CHECK_EXCEPTION(jni(), "error during CallVoidMethod");
532 }
533
534 virtual void OnSignalingChange(
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000535 PeerConnectionInterface::SignalingState new_state) OVERRIDE {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000536 jmethodID m = GetMethodID(
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000537 jni(), *j_observer_class_, "onSignalingChange",
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000538 "(Lorg/webrtc/PeerConnection$SignalingState;)V");
539 ScopedLocalRef<jobject> new_state_enum(jni(), JavaEnumFromIndex(
540 jni(), "PeerConnection$SignalingState", new_state));
541 jni()->CallVoidMethod(*j_observer_global_, m, *new_state_enum);
542 CHECK_EXCEPTION(jni(), "error during CallVoidMethod");
543 }
544
545 virtual void OnIceConnectionChange(
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000546 PeerConnectionInterface::IceConnectionState new_state) OVERRIDE {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000547 jmethodID m = GetMethodID(
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000548 jni(), *j_observer_class_, "onIceConnectionChange",
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000549 "(Lorg/webrtc/PeerConnection$IceConnectionState;)V");
550 ScopedLocalRef<jobject> new_state_enum(jni(), JavaEnumFromIndex(
551 jni(), "PeerConnection$IceConnectionState", new_state));
552 jni()->CallVoidMethod(*j_observer_global_, m, *new_state_enum);
553 CHECK_EXCEPTION(jni(), "error during CallVoidMethod");
554 }
555
556 virtual void OnIceGatheringChange(
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000557 PeerConnectionInterface::IceGatheringState new_state) OVERRIDE {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000558 jmethodID m = GetMethodID(
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000559 jni(), *j_observer_class_, "onIceGatheringChange",
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000560 "(Lorg/webrtc/PeerConnection$IceGatheringState;)V");
561 ScopedLocalRef<jobject> new_state_enum(jni(), JavaEnumFromIndex(
562 jni(), "PeerConnection$IceGatheringState", new_state));
563 jni()->CallVoidMethod(*j_observer_global_, m, *new_state_enum);
564 CHECK_EXCEPTION(jni(), "error during CallVoidMethod");
565 }
566
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000567 virtual void OnAddStream(MediaStreamInterface* stream) OVERRIDE {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000568 ScopedLocalRef<jobject> j_stream(jni(), jni()->NewObject(
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000569 *j_media_stream_class_, j_media_stream_ctor_, (jlong)stream));
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000570 CHECK_EXCEPTION(jni(), "error during NewObject");
571
572 AudioTrackVector audio_tracks = stream->GetAudioTracks();
573 for (size_t i = 0; i < audio_tracks.size(); ++i) {
574 AudioTrackInterface* track = audio_tracks[i];
575 ScopedLocalRef<jstring> id(
576 jni(), JavaStringFromStdString(jni(), track->id()));
577 ScopedLocalRef<jobject> j_track(jni(), jni()->NewObject(
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000578 *j_audio_track_class_, j_audio_track_ctor_, (jlong)track, *id));
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000579 CHECK_EXCEPTION(jni(), "error during NewObject");
fischman@webrtc.org32001ef2013-08-12 23:26:21 +0000580 jfieldID audio_tracks_id = GetFieldID(jni(),
581 *j_media_stream_class_,
582 "audioTracks",
583 "Ljava/util/LinkedList;");
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000584 ScopedLocalRef<jobject> audio_tracks(jni(), GetObjectField(
585 jni(), *j_stream, audio_tracks_id));
586 jmethodID add = GetMethodID(jni(),
587 GetObjectClass(jni(), *audio_tracks), "add", "(Ljava/lang/Object;)Z");
588 jboolean added = jni()->CallBooleanMethod(*audio_tracks, add, *j_track);
589 CHECK_EXCEPTION(jni(), "error during CallBooleanMethod");
590 CHECK(added, "");
591 }
592
593 VideoTrackVector video_tracks = stream->GetVideoTracks();
594 for (size_t i = 0; i < video_tracks.size(); ++i) {
595 VideoTrackInterface* track = video_tracks[i];
596 ScopedLocalRef<jstring> id(
597 jni(), JavaStringFromStdString(jni(), track->id()));
598 ScopedLocalRef<jobject> j_track(jni(), jni()->NewObject(
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000599 *j_video_track_class_, j_video_track_ctor_, (jlong)track, *id));
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000600 CHECK_EXCEPTION(jni(), "error during NewObject");
fischman@webrtc.org32001ef2013-08-12 23:26:21 +0000601 jfieldID video_tracks_id = GetFieldID(jni(),
602 *j_media_stream_class_,
603 "videoTracks",
604 "Ljava/util/LinkedList;");
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000605 ScopedLocalRef<jobject> video_tracks(jni(), GetObjectField(
606 jni(), *j_stream, video_tracks_id));
607 jmethodID add = GetMethodID(jni(),
608 GetObjectClass(jni(), *video_tracks), "add", "(Ljava/lang/Object;)Z");
609 jboolean added = jni()->CallBooleanMethod(*video_tracks, add, *j_track);
610 CHECK_EXCEPTION(jni(), "error during CallBooleanMethod");
611 CHECK(added, "");
612 }
613 streams_[stream] = jni()->NewWeakGlobalRef(*j_stream);
614 CHECK_EXCEPTION(jni(), "error during NewWeakGlobalRef");
615
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000616 jmethodID m = GetMethodID(jni(), *j_observer_class_, "onAddStream",
617 "(Lorg/webrtc/MediaStream;)V");
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000618 jni()->CallVoidMethod(*j_observer_global_, m, *j_stream);
619 CHECK_EXCEPTION(jni(), "error during CallVoidMethod");
620 }
621
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000622 virtual void OnRemoveStream(MediaStreamInterface* stream) OVERRIDE {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000623 NativeToJavaStreamsMap::iterator it = streams_.find(stream);
624 CHECK(it != streams_.end(), "unexpected stream: " << std::hex << stream);
625
626 WeakRef s(jni(), it->second);
627 streams_.erase(it);
628 if (!s.obj())
629 return;
630
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000631 jmethodID m = GetMethodID(jni(), *j_observer_class_, "onRemoveStream",
632 "(Lorg/webrtc/MediaStream;)V");
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000633 jni()->CallVoidMethod(*j_observer_global_, m, s.obj());
634 CHECK_EXCEPTION(jni(), "error during CallVoidMethod");
635 }
636
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000637 virtual void OnDataChannel(DataChannelInterface* channel) OVERRIDE {
638 ScopedLocalRef<jobject> j_channel(jni(), jni()->NewObject(
639 *j_data_channel_class_, j_data_channel_ctor_, (jlong)channel));
640 CHECK_EXCEPTION(jni(), "error during NewObject");
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000641
642 jmethodID m = GetMethodID(jni(), *j_observer_class_, "onDataChannel",
643 "(Lorg/webrtc/DataChannel;)V");
644 jni()->CallVoidMethod(*j_observer_global_, m, *j_channel);
fischman@webrtc.org32001ef2013-08-12 23:26:21 +0000645
646 // Channel is now owned by Java object, and will be freed from
647 // DataChannel.dispose(). Important that this be done _after_ the
648 // CallVoidMethod above as Java code might call back into native code and be
649 // surprised to see a refcount of 2.
650 int bumped_count = channel->AddRef();
651 CHECK(bumped_count == 2, "Unexpected refcount OnDataChannel");
652
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000653 CHECK_EXCEPTION(jni(), "error during CallVoidMethod");
654 }
655
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000656 void SetConstraints(ConstraintsWrapper* constraints) {
657 CHECK(!constraints_.get(), "constraints already set!");
658 constraints_.reset(constraints);
659 }
660
661 const ConstraintsWrapper* constraints() { return constraints_.get(); }
662
663 private:
664 JNIEnv* jni() {
665 return AttachCurrentThreadIfNeeded();
666 }
667
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000668 const ScopedGlobalRef<jobject> j_observer_global_;
669 const ScopedGlobalRef<jclass> j_observer_class_;
670 const ScopedGlobalRef<jclass> j_media_stream_class_;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000671 const jmethodID j_media_stream_ctor_;
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000672 const ScopedGlobalRef<jclass> j_audio_track_class_;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000673 const jmethodID j_audio_track_ctor_;
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000674 const ScopedGlobalRef<jclass> j_video_track_class_;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000675 const jmethodID j_video_track_ctor_;
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000676 const ScopedGlobalRef<jclass> j_data_channel_class_;
677 const jmethodID j_data_channel_ctor_;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000678 typedef std::map<void*, jweak> NativeToJavaStreamsMap;
679 NativeToJavaStreamsMap streams_; // C++ -> Java streams.
680 talk_base::scoped_ptr<ConstraintsWrapper> constraints_;
681};
682
683// Wrapper for a Java MediaConstraints object. Copies all needed data so when
684// the constructor returns the Java object is no longer needed.
685class ConstraintsWrapper : public MediaConstraintsInterface {
686 public:
687 ConstraintsWrapper(JNIEnv* jni, jobject j_constraints) {
688 PopulateConstraintsFromJavaPairList(
689 jni, j_constraints, "mandatory", &mandatory_);
690 PopulateConstraintsFromJavaPairList(
691 jni, j_constraints, "optional", &optional_);
692 }
693
694 virtual ~ConstraintsWrapper() {}
695
696 // MediaConstraintsInterface.
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000697 virtual const Constraints& GetMandatory() const OVERRIDE {
698 return mandatory_;
699 }
700
701 virtual const Constraints& GetOptional() const OVERRIDE {
702 return optional_;
703 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000704
705 private:
706 // Helper for translating a List<Pair<String, String>> to a Constraints.
707 static void PopulateConstraintsFromJavaPairList(
708 JNIEnv* jni, jobject j_constraints,
709 const char* field_name, Constraints* field) {
710 jfieldID j_id = GetFieldID(jni,
711 GetObjectClass(jni, j_constraints), field_name, "Ljava/util/List;");
712 jobject j_list = GetObjectField(jni, j_constraints, j_id);
713 jmethodID j_iterator_id = GetMethodID(jni,
714 GetObjectClass(jni, j_list), "iterator", "()Ljava/util/Iterator;");
715 jobject j_iterator = jni->CallObjectMethod(j_list, j_iterator_id);
716 CHECK_EXCEPTION(jni, "error during CallObjectMethod");
717 jmethodID j_has_next = GetMethodID(jni,
718 GetObjectClass(jni, j_iterator), "hasNext", "()Z");
719 jmethodID j_next = GetMethodID(jni,
720 GetObjectClass(jni, j_iterator), "next", "()Ljava/lang/Object;");
721 while (jni->CallBooleanMethod(j_iterator, j_has_next)) {
722 CHECK_EXCEPTION(jni, "error during CallBooleanMethod");
723 jobject entry = jni->CallObjectMethod(j_iterator, j_next);
724 CHECK_EXCEPTION(jni, "error during CallObjectMethod");
725 jmethodID get_key = GetMethodID(jni,
726 GetObjectClass(jni, entry), "getKey", "()Ljava/lang/String;");
727 jstring j_key = reinterpret_cast<jstring>(
728 jni->CallObjectMethod(entry, get_key));
729 CHECK_EXCEPTION(jni, "error during CallObjectMethod");
730 jmethodID get_value = GetMethodID(jni,
731 GetObjectClass(jni, entry), "getValue", "()Ljava/lang/String;");
732 jstring j_value = reinterpret_cast<jstring>(
733 jni->CallObjectMethod(entry, get_value));
734 CHECK_EXCEPTION(jni, "error during CallObjectMethod");
735 field->push_back(Constraint(JavaToStdString(jni, j_key),
736 JavaToStdString(jni, j_value)));
737 }
738 CHECK_EXCEPTION(jni, "error during CallBooleanMethod");
739 }
740
741 Constraints mandatory_;
742 Constraints optional_;
743};
744
745static jobject JavaSdpFromNativeSdp(
746 JNIEnv* jni, const SessionDescriptionInterface* desc) {
747 std::string sdp;
748 CHECK(desc->ToString(&sdp), "got so far: " << sdp);
749 ScopedLocalRef<jstring> j_description(jni, JavaStringFromStdString(jni, sdp));
750
751 jclass j_type_class = FindClass(
752 jni, "org/webrtc/SessionDescription$Type");
753 jmethodID j_type_from_canonical = GetStaticMethodID(
754 jni, j_type_class, "fromCanonicalForm",
755 "(Ljava/lang/String;)Lorg/webrtc/SessionDescription$Type;");
756 ScopedLocalRef<jstring> j_type_string(
757 jni, JavaStringFromStdString(jni, desc->type()));
758 jobject j_type = jni->CallStaticObjectMethod(
759 j_type_class, j_type_from_canonical, *j_type_string);
760 CHECK_EXCEPTION(jni, "error during CallObjectMethod");
761
762 jclass j_sdp_class = FindClass(jni, "org/webrtc/SessionDescription");
763 jmethodID j_sdp_ctor = GetMethodID(
764 jni, j_sdp_class, "<init>",
765 "(Lorg/webrtc/SessionDescription$Type;Ljava/lang/String;)V");
766 jobject j_sdp = jni->NewObject(
767 j_sdp_class, j_sdp_ctor, j_type, *j_description);
768 CHECK_EXCEPTION(jni, "error during NewObject");
769 return j_sdp;
770}
771
772template <class T> // T is one of {Create,Set}SessionDescriptionObserver.
773class SdpObserverWrapper : public T {
774 public:
775 SdpObserverWrapper(JNIEnv* jni, jobject j_observer,
776 ConstraintsWrapper* constraints)
777 : constraints_(constraints),
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000778 j_observer_global_(jni, j_observer),
779 j_observer_class_(jni, GetObjectClass(jni, j_observer)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000780 }
781
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000782 virtual ~SdpObserverWrapper() {}
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000783
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000784 // Can't mark OVERRIDE because of templating.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000785 virtual void OnSuccess() {
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000786 jmethodID m = GetMethodID(jni(), *j_observer_class_, "onSetSuccess", "()V");
787 jni()->CallVoidMethod(*j_observer_global_, m);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000788 CHECK_EXCEPTION(jni(), "error during CallVoidMethod");
789 }
790
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000791 // Can't mark OVERRIDE because of templating.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000792 virtual void OnSuccess(SessionDescriptionInterface* desc) {
793 jmethodID m = GetMethodID(
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000794 jni(), *j_observer_class_, "onCreateSuccess",
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000795 "(Lorg/webrtc/SessionDescription;)V");
796 ScopedLocalRef<jobject> j_sdp(jni(), JavaSdpFromNativeSdp(jni(), desc));
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000797 jni()->CallVoidMethod(*j_observer_global_, m, *j_sdp);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000798 CHECK_EXCEPTION(jni(), "error during CallVoidMethod");
799 }
800
801 protected:
802 // Common implementation for failure of Set & Create types, distinguished by
803 // |op| being "Set" or "Create".
804 void OnFailure(const std::string& op, const std::string& error) {
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000805 jmethodID m = GetMethodID(jni(), *j_observer_class_, "on" + op + "Failure",
806 "(Ljava/lang/String;)V");
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000807 ScopedLocalRef<jstring> j_error_string(
808 jni(), JavaStringFromStdString(jni(), error));
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000809 jni()->CallVoidMethod(*j_observer_global_, m, *j_error_string);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000810 CHECK_EXCEPTION(jni(), "error during CallVoidMethod");
811 }
812
813 private:
814 JNIEnv* jni() {
815 return AttachCurrentThreadIfNeeded();
816 }
817
818 talk_base::scoped_ptr<ConstraintsWrapper> constraints_;
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000819 const ScopedGlobalRef<jobject> j_observer_global_;
820 const ScopedGlobalRef<jclass> j_observer_class_;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000821};
822
823class CreateSdpObserverWrapper
824 : public SdpObserverWrapper<CreateSessionDescriptionObserver> {
825 public:
826 CreateSdpObserverWrapper(JNIEnv* jni, jobject j_observer,
827 ConstraintsWrapper* constraints)
828 : SdpObserverWrapper(jni, j_observer, constraints) {}
829
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000830 virtual void OnFailure(const std::string& error) OVERRIDE {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000831 SdpObserverWrapper::OnFailure(std::string("Create"), error);
832 }
833};
834
835class SetSdpObserverWrapper
836 : public SdpObserverWrapper<SetSessionDescriptionObserver> {
837 public:
838 SetSdpObserverWrapper(JNIEnv* jni, jobject j_observer,
839 ConstraintsWrapper* constraints)
840 : SdpObserverWrapper(jni, j_observer, constraints) {}
841
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000842 virtual void OnFailure(const std::string& error) OVERRIDE {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000843 SdpObserverWrapper::OnFailure(std::string("Set"), error);
844 }
845};
846
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000847// Adapter for a Java DataChannel$Observer presenting a C++ DataChannelObserver
848// and dispatching the callback from C++ back to Java.
849class DataChannelObserverWrapper : public DataChannelObserver {
850 public:
851 DataChannelObserverWrapper(JNIEnv* jni, jobject j_observer)
852 : j_observer_global_(jni, j_observer),
853 j_observer_class_(jni, GetObjectClass(jni, j_observer)),
854 j_on_state_change_mid_(GetMethodID(jni, *j_observer_class_,
855 "onStateChange", "()V")),
856 j_on_message_mid_(GetMethodID(jni, *j_observer_class_, "onMessage",
857 "(Lorg/webrtc/DataChannel$Buffer;)V")),
858 j_buffer_class_(jni, FindClass(jni, "org/webrtc/DataChannel$Buffer")),
859 j_buffer_ctor_(GetMethodID(jni, *j_buffer_class_,
860 "<init>", "(Ljava/nio/ByteBuffer;Z)V")) {
861 }
862
863 virtual ~DataChannelObserverWrapper() {}
864
865 virtual void OnStateChange() OVERRIDE {
866 jni()->CallVoidMethod(*j_observer_global_, j_on_state_change_mid_);
867 CHECK_EXCEPTION(jni(), "error during CallVoidMethod");
868 }
869
870 virtual void OnMessage(const DataBuffer& buffer) OVERRIDE {
871 jobject byte_buffer =
872 jni()->NewDirectByteBuffer(const_cast<char*>(buffer.data.data()),
873 buffer.data.length());
874 jobject j_buffer = jni()->NewObject(*j_buffer_class_, j_buffer_ctor_,
875 byte_buffer, buffer.binary);
876 jni()->CallVoidMethod(*j_observer_global_, j_on_message_mid_, j_buffer);
877 CHECK_EXCEPTION(jni(), "error during CallVoidMethod");
878 }
879
880 private:
881 JNIEnv* jni() {
882 return AttachCurrentThreadIfNeeded();
883 }
884
885 const ScopedGlobalRef<jobject> j_observer_global_;
886 const ScopedGlobalRef<jclass> j_observer_class_;
887 const ScopedGlobalRef<jclass> j_buffer_class_;
888 const jmethodID j_on_state_change_mid_;
889 const jmethodID j_on_message_mid_;
890 const jmethodID j_buffer_ctor_;
891};
892
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000893// Adapter for a Java StatsObserver presenting a C++ StatsObserver and
894// dispatching the callback from C++ back to Java.
895class StatsObserverWrapper : public StatsObserver {
896 public:
897 StatsObserverWrapper(JNIEnv* jni, jobject j_observer)
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000898 : j_observer_global_(jni, j_observer),
899 j_observer_class_(jni, GetObjectClass(jni, j_observer)),
900 j_stats_report_class_(jni, FindClass(jni, "org/webrtc/StatsReport")),
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000901 j_stats_report_ctor_(GetMethodID(
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000902 jni, *j_stats_report_class_, "<init>",
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000903 "(Ljava/lang/String;Ljava/lang/String;D"
904 "[Lorg/webrtc/StatsReport$Value;)V")),
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000905 j_value_class_(jni, FindClass(
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000906 jni, "org/webrtc/StatsReport$Value")),
907 j_value_ctor_(GetMethodID(
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000908 jni, *j_value_class_, "<init>",
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000909 "(Ljava/lang/String;Ljava/lang/String;)V")) {
910 }
911
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000912 virtual ~StatsObserverWrapper() {}
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000913
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000914 virtual void OnComplete(const std::vector<StatsReport>& reports) OVERRIDE {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000915 ScopedLocalRef<jobjectArray> j_reports(jni(),
916 ReportsToJava(jni(), reports));
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000917 jmethodID m = GetMethodID(jni(), *j_observer_class_, "onComplete",
918 "([Lorg/webrtc/StatsReport;)V");
919 jni()->CallVoidMethod(*j_observer_global_, m, *j_reports);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000920 CHECK_EXCEPTION(jni(), "error during CallVoidMethod");
921 }
922
923 private:
924 jobjectArray ReportsToJava(
925 JNIEnv* jni, const std::vector<StatsReport>& reports) {
926 jobjectArray reports_array = jni->NewObjectArray(
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000927 reports.size(), *j_stats_report_class_, NULL);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000928 for (int i = 0; i < reports.size(); ++i) {
929 const StatsReport& report = reports[i];
930 ScopedLocalRef<jstring> j_id(
931 jni, JavaStringFromStdString(jni, report.id));
932 ScopedLocalRef<jstring> j_type(
933 jni, JavaStringFromStdString(jni, report.type));
934 ScopedLocalRef<jobjectArray> j_values(
935 jni, ValuesToJava(jni, report.values));
936 ScopedLocalRef<jobject> j_report(jni, jni->NewObject(
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000937 *j_stats_report_class_, j_stats_report_ctor_, *j_id, *j_type,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000938 report.timestamp, *j_values));
939 jni->SetObjectArrayElement(reports_array, i, *j_report);
940 }
941 return reports_array;
942 }
943
944 jobjectArray ValuesToJava(JNIEnv* jni, const StatsReport::Values& values) {
945 jobjectArray j_values = jni->NewObjectArray(
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000946 values.size(), *j_value_class_, NULL);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000947 for (int i = 0; i < values.size(); ++i) {
948 const StatsReport::Value& value = values[i];
949 ScopedLocalRef<jstring> j_name(
950 jni, JavaStringFromStdString(jni, value.name));
951 ScopedLocalRef<jstring> j_value(
952 jni, JavaStringFromStdString(jni, value.value));
953 ScopedLocalRef<jobject> j_element_value(jni, jni->NewObject(
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000954 *j_value_class_, j_value_ctor_, *j_name, *j_value));
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000955 jni->SetObjectArrayElement(j_values, i, *j_element_value);
956 }
957 return j_values;
958 }
959
960 JNIEnv* jni() {
961 return AttachCurrentThreadIfNeeded();
962 }
963
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000964 const ScopedGlobalRef<jobject> j_observer_global_;
965 const ScopedGlobalRef<jclass> j_observer_class_;
966 const ScopedGlobalRef<jclass> j_stats_report_class_;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000967 const jmethodID j_stats_report_ctor_;
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000968 const ScopedGlobalRef<jclass> j_value_class_;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000969 const jmethodID j_value_ctor_;
970};
971
972// Adapter presenting a cricket::VideoRenderer as a
973// webrtc::VideoRendererInterface.
974class VideoRendererWrapper : public VideoRendererInterface {
975 public:
976 static VideoRendererWrapper* Create(cricket::VideoRenderer* renderer) {
977 if (renderer)
978 return new VideoRendererWrapper(renderer);
979 return NULL;
980 }
981
982 virtual ~VideoRendererWrapper() {}
983
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000984 virtual void SetSize(int width, int height) OVERRIDE {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000985 const bool kNotReserved = false; // What does this param mean??
986 renderer_->SetSize(width, height, kNotReserved);
987 }
988
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000989 virtual void RenderFrame(const cricket::VideoFrame* frame) OVERRIDE {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000990 renderer_->RenderFrame(frame);
991 }
992
993 private:
994 explicit VideoRendererWrapper(cricket::VideoRenderer* renderer)
995 : renderer_(renderer) {}
996
997 talk_base::scoped_ptr<cricket::VideoRenderer> renderer_;
998};
999
1000// Wrapper dispatching webrtc::VideoRendererInterface to a Java VideoRenderer
1001// instance.
1002class JavaVideoRendererWrapper : public VideoRendererInterface {
1003 public:
1004 JavaVideoRendererWrapper(JNIEnv* jni, jobject j_callbacks)
henrike@webrtc.org723d6832013-07-12 16:04:50 +00001005 : j_callbacks_(jni, j_callbacks),
1006 j_set_size_id_(GetMethodID(
1007 jni, GetObjectClass(jni, j_callbacks), "setSize", "(II)V")),
1008 j_render_frame_id_(GetMethodID(
1009 jni, GetObjectClass(jni, j_callbacks), "renderFrame",
1010 "(Lorg/webrtc/VideoRenderer$I420Frame;)V")),
1011 j_frame_class_(jni,
1012 FindClass(jni, "org/webrtc/VideoRenderer$I420Frame")),
1013 j_frame_ctor_id_(GetMethodID(
1014 jni, *j_frame_class_, "<init>", "(II[I[Ljava/nio/ByteBuffer;)V")),
1015 j_byte_buffer_class_(jni, FindClass(jni, "java/nio/ByteBuffer")) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001016 CHECK_EXCEPTION(jni, "");
1017 }
1018
henrike@webrtc.org723d6832013-07-12 16:04:50 +00001019 virtual ~JavaVideoRendererWrapper() {}
1020
1021 virtual void SetSize(int width, int height) OVERRIDE {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001022 jni()->CallVoidMethod(*j_callbacks_, j_set_size_id_, width, height);
1023 CHECK_EXCEPTION(jni(), "");
1024 }
1025
henrike@webrtc.org723d6832013-07-12 16:04:50 +00001026 virtual void RenderFrame(const cricket::VideoFrame* frame) OVERRIDE {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001027 ScopedLocalRef<jobject> j_frame(jni(), CricketToJavaFrame(frame));
1028 jni()->CallVoidMethod(*j_callbacks_, j_render_frame_id_, *j_frame);
1029 CHECK_EXCEPTION(jni(), "");
1030 }
1031
1032 private:
1033 // Return a VideoRenderer.I420Frame referring to the data in |frame|.
1034 jobject CricketToJavaFrame(const cricket::VideoFrame* frame) {
1035 ScopedLocalRef<jintArray> strides(jni(), jni()->NewIntArray(3));
1036 jint* strides_array = jni()->GetIntArrayElements(*strides, NULL);
1037 strides_array[0] = frame->GetYPitch();
1038 strides_array[1] = frame->GetUPitch();
1039 strides_array[2] = frame->GetVPitch();
1040 jni()->ReleaseIntArrayElements(*strides, strides_array, 0);
1041 ScopedLocalRef<jobjectArray> planes(
henrike@webrtc.org723d6832013-07-12 16:04:50 +00001042 jni(), jni()->NewObjectArray(3, *j_byte_buffer_class_, NULL));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001043 ScopedLocalRef<jobject> y_buffer(jni(), jni()->NewDirectByteBuffer(
1044 const_cast<uint8*>(frame->GetYPlane()),
1045 frame->GetYPitch() * frame->GetHeight()));
1046 ScopedLocalRef<jobject> u_buffer(jni(), jni()->NewDirectByteBuffer(
1047 const_cast<uint8*>(frame->GetUPlane()), frame->GetChromaSize()));
1048 ScopedLocalRef<jobject> v_buffer(jni(), jni()->NewDirectByteBuffer(
1049 const_cast<uint8*>(frame->GetVPlane()), frame->GetChromaSize()));
1050 jni()->SetObjectArrayElement(*planes, 0, *y_buffer);
1051 jni()->SetObjectArrayElement(*planes, 1, *u_buffer);
1052 jni()->SetObjectArrayElement(*planes, 2, *v_buffer);
1053 return jni()->NewObject(
henrike@webrtc.org723d6832013-07-12 16:04:50 +00001054 *j_frame_class_, j_frame_ctor_id_,
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001055 frame->GetWidth(), frame->GetHeight(), *strides, *planes);
1056 }
1057
1058 JNIEnv* jni() {
1059 return AttachCurrentThreadIfNeeded();
1060 }
1061
henrike@webrtc.org723d6832013-07-12 16:04:50 +00001062 ScopedGlobalRef<jobject> j_callbacks_;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001063 jmethodID j_set_size_id_;
1064 jmethodID j_render_frame_id_;
henrike@webrtc.org723d6832013-07-12 16:04:50 +00001065 ScopedGlobalRef<jclass> j_frame_class_;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001066 jmethodID j_frame_ctor_id_;
henrike@webrtc.org723d6832013-07-12 16:04:50 +00001067 ScopedGlobalRef<jclass> j_byte_buffer_class_;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001068};
1069
1070} // anonymous namespace
1071
1072
1073// Convenience macro defining JNI-accessible methods in the org.webrtc package.
1074// Eliminates unnecessary boilerplate and line-wraps, reducing visual clutter.
1075#define JOW(rettype, name) extern "C" rettype JNIEXPORT JNICALL \
1076 Java_org_webrtc_##name
1077
1078extern "C" jint JNIEXPORT JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved) {
1079 CHECK(!g_jvm, "JNI_OnLoad called more than once!");
1080 g_jvm = jvm;
1081 CHECK(g_jvm, "JNI_OnLoad handed NULL?");
1082
1083 CHECK(talk_base::InitializeSSL(), "Failed to InitializeSSL()");
1084
1085 JNIEnv* jni;
1086 if (jvm->GetEnv(reinterpret_cast<void**>(&jni), JNI_VERSION_1_6) != JNI_OK)
1087 return -1;
1088 g_class_reference_holder = new ClassReferenceHolder(jni);
1089
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001090 return JNI_VERSION_1_6;
1091}
1092
1093extern "C" void JNIEXPORT JNICALL JNI_OnUnLoad(JavaVM *jvm, void *reserved) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001094 delete g_class_reference_holder;
1095 g_class_reference_holder = NULL;
1096 CHECK(talk_base::CleanupSSL(), "Failed to CleanupSSL()");
1097}
1098
fischman@webrtc.org32001ef2013-08-12 23:26:21 +00001099static DataChannelInterface* ExtractNativeDC(JNIEnv* jni, jobject j_dc) {
henrike@webrtc.org723d6832013-07-12 16:04:50 +00001100 jfieldID native_dc_id = GetFieldID(jni,
1101 GetObjectClass(jni, j_dc), "nativeDataChannel", "J");
1102 jlong j_d = GetLongField(jni, j_dc, native_dc_id);
fischman@webrtc.org32001ef2013-08-12 23:26:21 +00001103 return reinterpret_cast<DataChannelInterface*>(j_d);
henrike@webrtc.org723d6832013-07-12 16:04:50 +00001104}
1105
1106JOW(jlong, DataChannel_registerObserverNative)(
1107 JNIEnv* jni, jobject j_dc, jobject j_observer) {
1108 talk_base::scoped_ptr<DataChannelObserverWrapper> observer(
1109 new DataChannelObserverWrapper(jni, j_observer));
1110 ExtractNativeDC(jni, j_dc)->RegisterObserver(observer.get());
1111 return reinterpret_cast<jlong>(observer.release());
1112}
1113
1114JOW(void, DataChannel_unregisterObserverNative)(
1115 JNIEnv* jni, jobject j_dc, jlong native_observer) {
1116 ExtractNativeDC(jni, j_dc)->UnregisterObserver();
1117 delete reinterpret_cast<DataChannelObserverWrapper*>(native_observer);
1118}
1119
1120JOW(jstring, DataChannel_label)(JNIEnv* jni, jobject j_dc) {
1121 return JavaStringFromStdString(jni, ExtractNativeDC(jni, j_dc)->label());
1122}
1123
1124JOW(jobject, DataChannel_state)(JNIEnv* jni, jobject j_dc) {
1125 return JavaEnumFromIndex(
1126 jni, "DataChannel$State", ExtractNativeDC(jni, j_dc)->state());
1127}
1128
1129JOW(jlong, DataChannel_bufferedAmount)(JNIEnv* jni, jobject j_dc) {
1130 uint64 buffered_amount = ExtractNativeDC(jni, j_dc)->buffered_amount();
1131 CHECK(buffered_amount <= std::numeric_limits<int64>::max(),
1132 "buffered_amount overflowed jlong!");
1133 return static_cast<jlong>(buffered_amount);
1134}
1135
1136JOW(void, DataChannel_close)(JNIEnv* jni, jobject j_dc) {
1137 ExtractNativeDC(jni, j_dc)->Close();
1138}
1139
1140JOW(jboolean, DataChannel_sendNative)(JNIEnv* jni, jobject j_dc,
1141 jbyteArray data, jboolean binary) {
1142 jbyte* bytes = jni->GetByteArrayElements(data, NULL);
1143 bool ret = ExtractNativeDC(jni, j_dc)->Send(DataBuffer(
1144 talk_base::Buffer(bytes, jni->GetArrayLength(data)),
1145 binary));
1146 jni->ReleaseByteArrayElements(data, bytes, JNI_ABORT);
1147 return ret;
1148}
1149
1150JOW(void, DataChannel_dispose)(JNIEnv* jni, jobject j_dc) {
fischman@webrtc.org32001ef2013-08-12 23:26:21 +00001151 CHECK_RELEASE(ExtractNativeDC(jni, j_dc));
henrike@webrtc.org723d6832013-07-12 16:04:50 +00001152}
1153
fischman@webrtc.orgc883fdc2013-08-06 19:00:53 +00001154JOW(void, Logging_nativeEnableTracing)(
1155 JNIEnv* jni, jclass, jstring j_path, jint nativeLevels,
1156 jint nativeSeverity) {
1157 std::string path = JavaToStdString(jni, j_path);
1158 if (nativeLevels != webrtc::kTraceNone) {
andrew@webrtc.org90805182013-09-05 16:40:43 +00001159 webrtc::Trace::set_level_filter(nativeLevels);
fischman@webrtc.org7e4d0df2013-10-01 02:40:43 +00001160#ifdef ANDROID
1161 if (path != "logcat:") {
1162#endif
1163 CHECK(webrtc::Trace::SetTraceFile(path.c_str(), false) == 0,
1164 "SetTraceFile failed");
1165#ifdef ANDROID
1166 } else {
1167 // Intentionally leak this to avoid needing to reason about its lifecycle.
1168 // It keeps no state and functions only as a dispatch point.
1169 static LogcatTraceContext* g_trace_callback = new LogcatTraceContext();
1170 }
1171#endif
fischman@webrtc.orgc883fdc2013-08-06 19:00:53 +00001172 }
1173 talk_base::LogMessage::LogToDebug(nativeSeverity);
1174}
1175
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001176JOW(void, PeerConnection_freePeerConnection)(JNIEnv*, jclass, jlong j_p) {
fischman@webrtc.org32001ef2013-08-12 23:26:21 +00001177 CHECK_RELEASE(reinterpret_cast<PeerConnectionInterface*>(j_p));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001178}
1179
1180JOW(void, PeerConnection_freeObserver)(JNIEnv*, jclass, jlong j_p) {
1181 PCOJava* p = reinterpret_cast<PCOJava*>(j_p);
1182 delete p;
1183}
1184
1185JOW(void, MediaSource_free)(JNIEnv*, jclass, jlong j_p) {
fischman@webrtc.org32001ef2013-08-12 23:26:21 +00001186 CHECK_RELEASE(reinterpret_cast<MediaSourceInterface*>(j_p));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001187}
1188
1189JOW(void, VideoCapturer_free)(JNIEnv*, jclass, jlong j_p) {
1190 delete reinterpret_cast<cricket::VideoCapturer*>(j_p);
1191}
1192
1193JOW(void, VideoRenderer_free)(JNIEnv*, jclass, jlong j_p) {
1194 delete reinterpret_cast<VideoRendererWrapper*>(j_p);
1195}
1196
1197JOW(void, MediaStreamTrack_free)(JNIEnv*, jclass, jlong j_p) {
fischman@webrtc.org32001ef2013-08-12 23:26:21 +00001198 CHECK_RELEASE(reinterpret_cast<MediaStreamTrackInterface*>(j_p));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001199}
1200
1201JOW(jboolean, MediaStream_nativeAddAudioTrack)(
1202 JNIEnv* jni, jclass, jlong pointer, jlong j_audio_track_pointer) {
fischman@webrtc.org32001ef2013-08-12 23:26:21 +00001203 return reinterpret_cast<MediaStreamInterface*>(pointer)->AddTrack(
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001204 reinterpret_cast<AudioTrackInterface*>(j_audio_track_pointer));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001205}
1206
1207JOW(jboolean, MediaStream_nativeAddVideoTrack)(
1208 JNIEnv* jni, jclass, jlong pointer, jlong j_video_track_pointer) {
fischman@webrtc.org32001ef2013-08-12 23:26:21 +00001209 return reinterpret_cast<MediaStreamInterface*>(pointer)
1210 ->AddTrack(reinterpret_cast<VideoTrackInterface*>(j_video_track_pointer));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001211}
1212
1213JOW(jboolean, MediaStream_nativeRemoveAudioTrack)(
1214 JNIEnv* jni, jclass, jlong pointer, jlong j_audio_track_pointer) {
fischman@webrtc.org32001ef2013-08-12 23:26:21 +00001215 return reinterpret_cast<MediaStreamInterface*>(pointer)->RemoveTrack(
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001216 reinterpret_cast<AudioTrackInterface*>(j_audio_track_pointer));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001217}
1218
1219JOW(jboolean, MediaStream_nativeRemoveVideoTrack)(
1220 JNIEnv* jni, jclass, jlong pointer, jlong j_video_track_pointer) {
fischman@webrtc.org32001ef2013-08-12 23:26:21 +00001221 return reinterpret_cast<MediaStreamInterface*>(pointer)->RemoveTrack(
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001222 reinterpret_cast<VideoTrackInterface*>(j_video_track_pointer));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001223}
1224
1225JOW(jstring, MediaStream_nativeLabel)(JNIEnv* jni, jclass, jlong j_p) {
1226 return JavaStringFromStdString(
1227 jni, reinterpret_cast<MediaStreamInterface*>(j_p)->label());
1228}
1229
1230JOW(void, MediaStream_free)(JNIEnv*, jclass, jlong j_p) {
fischman@webrtc.org32001ef2013-08-12 23:26:21 +00001231 CHECK_RELEASE(reinterpret_cast<MediaStreamInterface*>(j_p));
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001232}
1233
1234JOW(jlong, PeerConnectionFactory_nativeCreateObserver)(
1235 JNIEnv * jni, jclass, jobject j_observer) {
1236 return (jlong)new PCOJava(jni, j_observer);
1237}
1238
1239#ifdef ANDROID
1240JOW(jboolean, PeerConnectionFactory_initializeAndroidGlobals)(
1241 JNIEnv* jni, jclass, jobject context) {
1242 CHECK(g_jvm, "JNI_OnLoad failed to run?");
1243 bool failure = false;
fischman@webrtc.org4e65e072013-10-03 18:23:13 +00001244 failure |= webrtc::VideoEngine::SetAndroidObjects(g_jvm);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001245 failure |= webrtc::VoiceEngine::SetAndroidObjects(g_jvm, jni, context);
1246 return !failure;
1247}
1248#endif // ANDROID
1249
1250JOW(jlong, PeerConnectionFactory_nativeCreatePeerConnectionFactory)(
1251 JNIEnv* jni, jclass) {
fischman@webrtc.org32001ef2013-08-12 23:26:21 +00001252 webrtc::Trace::CreateTrace();
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001253 talk_base::scoped_refptr<PeerConnectionFactoryInterface> factory(
1254 webrtc::CreatePeerConnectionFactory());
1255 return (jlong)factory.release();
1256}
1257
1258JOW(void, PeerConnectionFactory_freeFactory)(JNIEnv*, jclass, jlong j_p) {
fischman@webrtc.org32001ef2013-08-12 23:26:21 +00001259 CHECK_RELEASE(reinterpret_cast<PeerConnectionFactoryInterface*>(j_p));
1260 webrtc::Trace::ReturnTrace();
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001261}
1262
1263JOW(jlong, PeerConnectionFactory_nativeCreateLocalMediaStream)(
1264 JNIEnv* jni, jclass, jlong native_factory, jstring label) {
1265 talk_base::scoped_refptr<PeerConnectionFactoryInterface> factory(
1266 reinterpret_cast<PeerConnectionFactoryInterface*>(native_factory));
1267 talk_base::scoped_refptr<MediaStreamInterface> stream(
1268 factory->CreateLocalMediaStream(JavaToStdString(jni, label)));
1269 return (jlong)stream.release();
1270}
1271
1272JOW(jlong, PeerConnectionFactory_nativeCreateVideoSource)(
1273 JNIEnv* jni, jclass, jlong native_factory, jlong native_capturer,
1274 jobject j_constraints) {
1275 talk_base::scoped_ptr<ConstraintsWrapper> constraints(
1276 new ConstraintsWrapper(jni, j_constraints));
1277 talk_base::scoped_refptr<PeerConnectionFactoryInterface> factory(
1278 reinterpret_cast<PeerConnectionFactoryInterface*>(native_factory));
1279 talk_base::scoped_refptr<VideoSourceInterface> source(
1280 factory->CreateVideoSource(
1281 reinterpret_cast<cricket::VideoCapturer*>(native_capturer),
1282 constraints.get()));
1283 return (jlong)source.release();
1284}
1285
1286JOW(jlong, PeerConnectionFactory_nativeCreateVideoTrack)(
1287 JNIEnv* jni, jclass, jlong native_factory, jstring id,
1288 jlong native_source) {
1289 talk_base::scoped_refptr<PeerConnectionFactoryInterface> factory(
1290 reinterpret_cast<PeerConnectionFactoryInterface*>(native_factory));
1291 talk_base::scoped_refptr<VideoTrackInterface> track(
1292 factory->CreateVideoTrack(
1293 JavaToStdString(jni, id),
1294 reinterpret_cast<VideoSourceInterface*>(native_source)));
1295 return (jlong)track.release();
1296}
1297
1298JOW(jlong, PeerConnectionFactory_nativeCreateAudioTrack)(
1299 JNIEnv* jni, jclass, jlong native_factory, jstring id) {
1300 talk_base::scoped_refptr<PeerConnectionFactoryInterface> factory(
1301 reinterpret_cast<PeerConnectionFactoryInterface*>(native_factory));
1302 talk_base::scoped_refptr<AudioTrackInterface> track(
1303 factory->CreateAudioTrack(JavaToStdString(jni, id), NULL));
1304 return (jlong)track.release();
1305}
1306
1307static void JavaIceServersToJsepIceServers(
1308 JNIEnv* jni, jobject j_ice_servers,
1309 PeerConnectionInterface::IceServers* ice_servers) {
1310 jclass list_class = GetObjectClass(jni, j_ice_servers);
1311 jmethodID iterator_id = GetMethodID(
1312 jni, list_class, "iterator", "()Ljava/util/Iterator;");
1313 jobject iterator = jni->CallObjectMethod(j_ice_servers, iterator_id);
1314 CHECK_EXCEPTION(jni, "error during CallObjectMethod");
1315 jmethodID iterator_has_next = GetMethodID(
1316 jni, GetObjectClass(jni, iterator), "hasNext", "()Z");
1317 jmethodID iterator_next = GetMethodID(
1318 jni, GetObjectClass(jni, iterator), "next", "()Ljava/lang/Object;");
1319 while (jni->CallBooleanMethod(iterator, iterator_has_next)) {
1320 CHECK_EXCEPTION(jni, "error during CallBooleanMethod");
1321 jobject j_ice_server = jni->CallObjectMethod(iterator, iterator_next);
1322 CHECK_EXCEPTION(jni, "error during CallObjectMethod");
1323 jclass j_ice_server_class = GetObjectClass(jni, j_ice_server);
1324 jfieldID j_ice_server_uri_id =
1325 GetFieldID(jni, j_ice_server_class, "uri", "Ljava/lang/String;");
1326 jfieldID j_ice_server_username_id =
1327 GetFieldID(jni, j_ice_server_class, "username", "Ljava/lang/String;");
1328 jfieldID j_ice_server_password_id =
1329 GetFieldID(jni, j_ice_server_class, "password", "Ljava/lang/String;");
1330 jstring uri = reinterpret_cast<jstring>(
1331 GetObjectField(jni, j_ice_server, j_ice_server_uri_id));
1332 jstring username = reinterpret_cast<jstring>(
1333 GetObjectField(jni, j_ice_server, j_ice_server_username_id));
1334 jstring password = reinterpret_cast<jstring>(
1335 GetObjectField(jni, j_ice_server, j_ice_server_password_id));
1336 PeerConnectionInterface::IceServer server;
1337 server.uri = JavaToStdString(jni, uri);
1338 server.username = JavaToStdString(jni, username);
1339 server.password = JavaToStdString(jni, password);
1340 ice_servers->push_back(server);
1341 }
1342 CHECK_EXCEPTION(jni, "error during CallBooleanMethod");
1343}
1344
1345JOW(jlong, PeerConnectionFactory_nativeCreatePeerConnection)(
1346 JNIEnv *jni, jclass, jlong factory, jobject j_ice_servers,
1347 jobject j_constraints, jlong observer_p) {
1348 talk_base::scoped_refptr<PeerConnectionFactoryInterface> f(
1349 reinterpret_cast<PeerConnectionFactoryInterface*>(factory));
1350 PeerConnectionInterface::IceServers servers;
1351 JavaIceServersToJsepIceServers(jni, j_ice_servers, &servers);
1352 PCOJava* observer = reinterpret_cast<PCOJava*>(observer_p);
1353 observer->SetConstraints(new ConstraintsWrapper(jni, j_constraints));
1354 talk_base::scoped_refptr<PeerConnectionInterface> pc(f->CreatePeerConnection(
1355 servers, observer->constraints(), NULL, observer));
1356 return (jlong)pc.release();
1357}
1358
1359static talk_base::scoped_refptr<PeerConnectionInterface> ExtractNativePC(
1360 JNIEnv* jni, jobject j_pc) {
1361 jfieldID native_pc_id = GetFieldID(jni,
1362 GetObjectClass(jni, j_pc), "nativePeerConnection", "J");
1363 jlong j_p = GetLongField(jni, j_pc, native_pc_id);
1364 return talk_base::scoped_refptr<PeerConnectionInterface>(
1365 reinterpret_cast<PeerConnectionInterface*>(j_p));
1366}
1367
1368JOW(jobject, PeerConnection_getLocalDescription)(JNIEnv* jni, jobject j_pc) {
1369 const SessionDescriptionInterface* sdp =
1370 ExtractNativePC(jni, j_pc)->local_description();
1371 return sdp ? JavaSdpFromNativeSdp(jni, sdp) : NULL;
1372}
1373
1374JOW(jobject, PeerConnection_getRemoteDescription)(JNIEnv* jni, jobject j_pc) {
1375 const SessionDescriptionInterface* sdp =
1376 ExtractNativePC(jni, j_pc)->remote_description();
1377 return sdp ? JavaSdpFromNativeSdp(jni, sdp) : NULL;
1378}
1379
henrike@webrtc.org723d6832013-07-12 16:04:50 +00001380JOW(jobject, PeerConnection_createDataChannel)(
1381 JNIEnv* jni, jobject j_pc, jstring j_label, jobject j_init) {
1382 DataChannelInit init = JavaDataChannelInitToNative(jni, j_init);
1383 talk_base::scoped_refptr<DataChannelInterface> channel(
1384 ExtractNativePC(jni, j_pc)->CreateDataChannel(
1385 JavaToStdString(jni, j_label), &init));
fischman@webrtc.org87881672013-09-03 18:58:12 +00001386 // Mustn't pass channel.get() directly through NewObject to avoid reading its
1387 // vararg parameter as 64-bit and reading memory that doesn't belong to the
1388 // 32-bit parameter.
1389 jlong nativeChannelPtr = jlongFromPointer(channel.get());
1390 CHECK(nativeChannelPtr, "Failed to create DataChannel");
henrike@webrtc.org723d6832013-07-12 16:04:50 +00001391 jclass j_data_channel_class = FindClass(jni, "org/webrtc/DataChannel");
1392 jmethodID j_data_channel_ctor = GetMethodID(
1393 jni, j_data_channel_class, "<init>", "(J)V");
1394 jobject j_channel = jni->NewObject(
fischman@webrtc.org87881672013-09-03 18:58:12 +00001395 j_data_channel_class, j_data_channel_ctor, nativeChannelPtr);
henrike@webrtc.org723d6832013-07-12 16:04:50 +00001396 CHECK_EXCEPTION(jni, "error during NewObject");
1397 // Channel is now owned by Java object, and will be freed from there.
fischman@webrtc.org32001ef2013-08-12 23:26:21 +00001398 int bumped_count = channel->AddRef();
1399 CHECK(bumped_count == 2, "Unexpected refcount");
henrike@webrtc.org723d6832013-07-12 16:04:50 +00001400 return j_channel;
1401}
1402
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001403JOW(void, PeerConnection_createOffer)(
1404 JNIEnv* jni, jobject j_pc, jobject j_observer, jobject j_constraints) {
1405 ConstraintsWrapper* constraints =
1406 new ConstraintsWrapper(jni, j_constraints);
1407 talk_base::scoped_refptr<CreateSdpObserverWrapper> observer(
1408 new talk_base::RefCountedObject<CreateSdpObserverWrapper>(
1409 jni, j_observer, constraints));
1410 ExtractNativePC(jni, j_pc)->CreateOffer(observer, constraints);
1411}
1412
1413JOW(void, PeerConnection_createAnswer)(
1414 JNIEnv* jni, jobject j_pc, jobject j_observer, jobject j_constraints) {
1415 ConstraintsWrapper* constraints =
1416 new ConstraintsWrapper(jni, j_constraints);
1417 talk_base::scoped_refptr<CreateSdpObserverWrapper> observer(
1418 new talk_base::RefCountedObject<CreateSdpObserverWrapper>(
1419 jni, j_observer, constraints));
1420 ExtractNativePC(jni, j_pc)->CreateAnswer(observer, constraints);
1421}
1422
1423// Helper to create a SessionDescriptionInterface from a SessionDescription.
1424static SessionDescriptionInterface* JavaSdpToNativeSdp(
1425 JNIEnv* jni, jobject j_sdp) {
1426 jfieldID j_type_id = GetFieldID(
1427 jni, GetObjectClass(jni, j_sdp), "type",
1428 "Lorg/webrtc/SessionDescription$Type;");
1429 jobject j_type = GetObjectField(jni, j_sdp, j_type_id);
1430 jmethodID j_canonical_form_id = GetMethodID(
1431 jni, GetObjectClass(jni, j_type), "canonicalForm",
1432 "()Ljava/lang/String;");
1433 jstring j_type_string = (jstring)jni->CallObjectMethod(
1434 j_type, j_canonical_form_id);
1435 CHECK_EXCEPTION(jni, "error during CallObjectMethod");
1436 std::string std_type = JavaToStdString(jni, j_type_string);
1437
1438 jfieldID j_description_id = GetFieldID(
1439 jni, GetObjectClass(jni, j_sdp), "description", "Ljava/lang/String;");
1440 jstring j_description = (jstring)GetObjectField(jni, j_sdp, j_description_id);
1441 std::string std_description = JavaToStdString(jni, j_description);
1442
1443 return webrtc::CreateSessionDescription(
1444 std_type, std_description, NULL);
1445}
1446
1447JOW(void, PeerConnection_setLocalDescription)(
1448 JNIEnv* jni, jobject j_pc,
1449 jobject j_observer, jobject j_sdp) {
1450 talk_base::scoped_refptr<SetSdpObserverWrapper> observer(
1451 new talk_base::RefCountedObject<SetSdpObserverWrapper>(
1452 jni, j_observer, reinterpret_cast<ConstraintsWrapper*>(NULL)));
1453 ExtractNativePC(jni, j_pc)->SetLocalDescription(
1454 observer, JavaSdpToNativeSdp(jni, j_sdp));
1455}
1456
1457JOW(void, PeerConnection_setRemoteDescription)(
1458 JNIEnv* jni, jobject j_pc,
1459 jobject j_observer, jobject j_sdp) {
1460 talk_base::scoped_refptr<SetSdpObserverWrapper> observer(
1461 new talk_base::RefCountedObject<SetSdpObserverWrapper>(
1462 jni, j_observer, reinterpret_cast<ConstraintsWrapper*>(NULL)));
1463 ExtractNativePC(jni, j_pc)->SetRemoteDescription(
1464 observer, JavaSdpToNativeSdp(jni, j_sdp));
1465}
1466
1467JOW(jboolean, PeerConnection_updateIce)(
1468 JNIEnv* jni, jobject j_pc, jobject j_ice_servers, jobject j_constraints) {
1469 PeerConnectionInterface::IceServers ice_servers;
1470 JavaIceServersToJsepIceServers(jni, j_ice_servers, &ice_servers);
1471 talk_base::scoped_ptr<ConstraintsWrapper> constraints(
1472 new ConstraintsWrapper(jni, j_constraints));
1473 return ExtractNativePC(jni, j_pc)->UpdateIce(ice_servers, constraints.get());
1474}
1475
1476JOW(jboolean, PeerConnection_nativeAddIceCandidate)(
1477 JNIEnv* jni, jobject j_pc, jstring j_sdp_mid,
1478 jint j_sdp_mline_index, jstring j_candidate_sdp) {
1479 std::string sdp_mid = JavaToStdString(jni, j_sdp_mid);
1480 std::string sdp = JavaToStdString(jni, j_candidate_sdp);
1481 talk_base::scoped_ptr<IceCandidateInterface> candidate(
1482 webrtc::CreateIceCandidate(sdp_mid, j_sdp_mline_index, sdp, NULL));
1483 return ExtractNativePC(jni, j_pc)->AddIceCandidate(candidate.get());
1484}
1485
1486JOW(jboolean, PeerConnection_nativeAddLocalStream)(
1487 JNIEnv* jni, jobject j_pc, jlong native_stream, jobject j_constraints) {
1488 talk_base::scoped_ptr<ConstraintsWrapper> constraints(
1489 new ConstraintsWrapper(jni, j_constraints));
1490 return ExtractNativePC(jni, j_pc)->AddStream(
1491 reinterpret_cast<MediaStreamInterface*>(native_stream),
1492 constraints.get());
1493}
1494
1495JOW(void, PeerConnection_nativeRemoveLocalStream)(
1496 JNIEnv* jni, jobject j_pc, jlong native_stream) {
1497 ExtractNativePC(jni, j_pc)->RemoveStream(
1498 reinterpret_cast<MediaStreamInterface*>(native_stream));
1499}
1500
1501JOW(bool, PeerConnection_nativeGetStats)(
1502 JNIEnv* jni, jobject j_pc, jobject j_observer, jlong native_track) {
1503 talk_base::scoped_refptr<StatsObserverWrapper> observer(
1504 new talk_base::RefCountedObject<StatsObserverWrapper>(jni, j_observer));
1505 return ExtractNativePC(jni, j_pc)->GetStats(
1506 observer, reinterpret_cast<MediaStreamTrackInterface*>(native_track));
1507}
1508
1509JOW(jobject, PeerConnection_signalingState)(JNIEnv* jni, jobject j_pc) {
1510 PeerConnectionInterface::SignalingState state =
1511 ExtractNativePC(jni, j_pc)->signaling_state();
1512 return JavaEnumFromIndex(jni, "PeerConnection$SignalingState", state);
1513}
1514
1515JOW(jobject, PeerConnection_iceConnectionState)(JNIEnv* jni, jobject j_pc) {
1516 PeerConnectionInterface::IceConnectionState state =
1517 ExtractNativePC(jni, j_pc)->ice_connection_state();
1518 return JavaEnumFromIndex(jni, "PeerConnection$IceConnectionState", state);
1519}
1520
1521JOW(jobject, PeerGathering_iceGatheringState)(JNIEnv* jni, jobject j_pc) {
1522 PeerConnectionInterface::IceGatheringState state =
1523 ExtractNativePC(jni, j_pc)->ice_gathering_state();
1524 return JavaEnumFromIndex(jni, "PeerGathering$IceGatheringState", state);
1525}
1526
1527JOW(void, PeerConnection_close)(JNIEnv* jni, jobject j_pc) {
1528 ExtractNativePC(jni, j_pc)->Close();
1529 return;
1530}
1531
1532JOW(jobject, MediaSource_nativeState)(JNIEnv* jni, jclass, jlong j_p) {
1533 talk_base::scoped_refptr<MediaSourceInterface> p(
1534 reinterpret_cast<MediaSourceInterface*>(j_p));
1535 return JavaEnumFromIndex(jni, "MediaSource$State", p->state());
1536}
1537
1538JOW(jlong, VideoCapturer_nativeCreateVideoCapturer)(
1539 JNIEnv* jni, jclass, jstring j_device_name) {
1540 std::string device_name = JavaToStdString(jni, j_device_name);
1541 talk_base::scoped_ptr<cricket::DeviceManagerInterface> device_manager(
1542 cricket::DeviceManagerFactory::Create());
1543 CHECK(device_manager->Init(), "DeviceManager::Init() failed");
1544 cricket::Device device;
1545 if (!device_manager->GetVideoCaptureDevice(device_name, &device)) {
fischman@webrtc.org4e65e072013-10-03 18:23:13 +00001546 LOG(LS_ERROR) << "GetVideoCaptureDevice failed for " << device_name;
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001547 return 0;
1548 }
1549 talk_base::scoped_ptr<cricket::VideoCapturer> capturer(
1550 device_manager->CreateVideoCapturer(device));
1551 return (jlong)capturer.release();
1552}
1553
1554JOW(jlong, VideoRenderer_nativeCreateGuiVideoRenderer)(
1555 JNIEnv* jni, jclass, int x, int y) {
1556 talk_base::scoped_ptr<VideoRendererWrapper> renderer(
1557 VideoRendererWrapper::Create(
1558 cricket::VideoRendererFactory::CreateGuiVideoRenderer(x, y)));
1559 return (jlong)renderer.release();
1560}
1561
1562JOW(jlong, VideoRenderer_nativeWrapVideoRenderer)(
1563 JNIEnv* jni, jclass, jobject j_callbacks) {
1564 talk_base::scoped_ptr<JavaVideoRendererWrapper> renderer(
1565 new JavaVideoRendererWrapper(jni, j_callbacks));
1566 return (jlong)renderer.release();
1567}
1568
fischman@webrtc.org4e65e072013-10-03 18:23:13 +00001569JOW(jlong, VideoSource_stop)(JNIEnv* jni, jclass, jlong j_p) {
1570 cricket::VideoCapturer* capturer =
1571 reinterpret_cast<VideoSourceInterface*>(j_p)->GetVideoCapturer();
1572 talk_base::scoped_ptr<cricket::VideoFormatPod> format(
1573 new cricket::VideoFormatPod(*capturer->GetCaptureFormat()));
1574 capturer->Stop();
1575 return jlongFromPointer(format.release());
1576}
1577
1578JOW(void, VideoSource_restart)(
1579 JNIEnv* jni, jclass, jlong j_p_source, jlong j_p_format) {
1580 talk_base::scoped_ptr<cricket::VideoFormatPod> format(
1581 reinterpret_cast<cricket::VideoFormatPod*>(j_p_format));
1582 reinterpret_cast<VideoSourceInterface*>(j_p_source)->GetVideoCapturer()->
1583 StartCapturing(cricket::VideoFormat(*format));
1584}
1585
1586JOW(jboolean, VideoSource_freeNativeVideoFormat)(
1587 JNIEnv* jni, jclass, jlong j_p) {
1588 delete reinterpret_cast<cricket::VideoFormatPod*>(j_p);
1589}
1590
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001591JOW(jstring, MediaStreamTrack_nativeId)(JNIEnv* jni, jclass, jlong j_p) {
fischman@webrtc.org32001ef2013-08-12 23:26:21 +00001592 return JavaStringFromStdString(
1593 jni, reinterpret_cast<MediaStreamTrackInterface*>(j_p)->id());
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001594}
1595
1596JOW(jstring, MediaStreamTrack_nativeKind)(JNIEnv* jni, jclass, jlong j_p) {
fischman@webrtc.org32001ef2013-08-12 23:26:21 +00001597 return JavaStringFromStdString(
1598 jni, reinterpret_cast<MediaStreamTrackInterface*>(j_p)->kind());
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001599}
1600
1601JOW(jboolean, MediaStreamTrack_nativeEnabled)(JNIEnv* jni, jclass, jlong j_p) {
fischman@webrtc.org32001ef2013-08-12 23:26:21 +00001602 return reinterpret_cast<MediaStreamTrackInterface*>(j_p)->enabled();
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001603}
1604
1605JOW(jobject, MediaStreamTrack_nativeState)(JNIEnv* jni, jclass, jlong j_p) {
fischman@webrtc.org32001ef2013-08-12 23:26:21 +00001606 return JavaEnumFromIndex(
1607 jni,
1608 "MediaStreamTrack$State",
1609 reinterpret_cast<MediaStreamTrackInterface*>(j_p)->state());
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001610}
1611
1612JOW(jboolean, MediaStreamTrack_nativeSetState)(
1613 JNIEnv* jni, jclass, jlong j_p, jint j_new_state) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001614 MediaStreamTrackInterface::TrackState new_state =
1615 (MediaStreamTrackInterface::TrackState)j_new_state;
fischman@webrtc.org32001ef2013-08-12 23:26:21 +00001616 return reinterpret_cast<MediaStreamTrackInterface*>(j_p)
1617 ->set_state(new_state);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001618}
1619
1620JOW(jboolean, MediaStreamTrack_nativeSetEnabled)(
1621 JNIEnv* jni, jclass, jlong j_p, jboolean enabled) {
fischman@webrtc.org32001ef2013-08-12 23:26:21 +00001622 return reinterpret_cast<MediaStreamTrackInterface*>(j_p)
1623 ->set_enabled(enabled);
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001624}
1625
1626JOW(void, VideoTrack_nativeAddRenderer)(
1627 JNIEnv* jni, jclass,
1628 jlong j_video_track_pointer, jlong j_renderer_pointer) {
fischman@webrtc.org32001ef2013-08-12 23:26:21 +00001629 reinterpret_cast<VideoTrackInterface*>(j_video_track_pointer)->AddRenderer(
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001630 reinterpret_cast<VideoRendererInterface*>(j_renderer_pointer));
1631}
1632
1633JOW(void, VideoTrack_nativeRemoveRenderer)(
1634 JNIEnv* jni, jclass,
1635 jlong j_video_track_pointer, jlong j_renderer_pointer) {
fischman@webrtc.org32001ef2013-08-12 23:26:21 +00001636 reinterpret_cast<VideoTrackInterface*>(j_video_track_pointer)->RemoveRenderer(
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001637 reinterpret_cast<VideoRendererInterface*>(j_renderer_pointer));
1638}