blob: fa3ad1b451a8e06bc17a7817f2830d36b6bee73a [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
henrike@webrtc.org723d6832013-07-12 16:04:50 +000059#include <limits>
henrike@webrtc.org28e20752013-07-10 00:45:36 +000060#include <map>
61
62#include "talk/app/webrtc/mediaconstraintsinterface.h"
63#include "talk/app/webrtc/peerconnectioninterface.h"
64#include "talk/app/webrtc/videosourceinterface.h"
65#include "talk/base/logging.h"
66#include "talk/base/ssladapter.h"
67#include "talk/media/base/videocapturer.h"
68#include "talk/media/base/videorenderer.h"
69#include "talk/media/devices/videorendererfactory.h"
70#include "talk/media/webrtc/webrtcvideocapturer.h"
fischman@webrtc.org3d496fb2013-07-30 17:14:35 +000071#include "third_party/icu/source/common/unicode/unistr.h"
henrike@webrtc.org9de257d2013-07-17 14:42:53 +000072#include "webrtc/system_wrappers/interface/trace.h"
73#include "webrtc/video_engine/include/vie_base.h"
74#include "webrtc/voice_engine/include/voe_base.h"
henrike@webrtc.org28e20752013-07-10 00:45:36 +000075
76using icu::UnicodeString;
77using webrtc::AudioSourceInterface;
78using webrtc::AudioTrackInterface;
79using webrtc::AudioTrackVector;
80using webrtc::CreateSessionDescriptionObserver;
henrike@webrtc.org723d6832013-07-12 16:04:50 +000081using webrtc::DataBuffer;
82using webrtc::DataChannelInit;
83using webrtc::DataChannelInterface;
84using webrtc::DataChannelObserver;
henrike@webrtc.org28e20752013-07-10 00:45:36 +000085using webrtc::IceCandidateInterface;
86using webrtc::MediaConstraintsInterface;
87using webrtc::MediaSourceInterface;
88using webrtc::MediaStreamInterface;
89using webrtc::MediaStreamTrackInterface;
90using webrtc::PeerConnectionFactoryInterface;
91using webrtc::PeerConnectionInterface;
92using webrtc::PeerConnectionObserver;
93using webrtc::SessionDescriptionInterface;
94using webrtc::SetSessionDescriptionObserver;
95using webrtc::StatsObserver;
96using webrtc::StatsReport;
97using webrtc::VideoRendererInterface;
98using webrtc::VideoSourceInterface;
99using webrtc::VideoTrackInterface;
100using webrtc::VideoTrackVector;
101using webrtc::VideoRendererInterface;
102
103// Abort the process if |x| is false, emitting |msg|.
104#define CHECK(x, msg) \
105 if (x) {} else { \
106 LOG(LS_ERROR) << __FILE__ << ":" << __LINE__ << ": " << msg; \
107 abort(); \
108 }
109// Abort the process if |jni| has a Java exception pending, emitting |msg|.
110#define CHECK_EXCEPTION(jni, msg) \
111 if (0) {} else { \
112 if (jni->ExceptionCheck()) { \
113 jni->ExceptionDescribe(); \
114 jni->ExceptionClear(); \
115 CHECK(0, msg); \
116 } \
117 }
118
119namespace {
120
121static JavaVM* g_jvm = NULL; // Set in JNI_OnLoad().
122
123static pthread_once_t g_jni_ptr_once = PTHREAD_ONCE_INIT;
124static pthread_key_t g_jni_ptr; // Key for per-thread JNIEnv* data.
125
126static void ThreadDestructor(void* unused) {
127 jint status = g_jvm->DetachCurrentThread();
128 CHECK(status == JNI_OK, "Failed to detach thread: " << status);
129}
130
131static void CreateJNIPtrKey() {
132 CHECK(!pthread_key_create(&g_jni_ptr, &ThreadDestructor),
133 "pthread_key_create");
134}
135
136// Deal with difference in signatures between Oracle's jni.h and Android's.
137static JNIEnv* AttachCurrentThreadIfNeeded() {
138 CHECK(!pthread_once(&g_jni_ptr_once, &CreateJNIPtrKey),
139 "pthread_once");
140 JNIEnv* jni = reinterpret_cast<JNIEnv*>(pthread_getspecific(g_jni_ptr));
141 if (jni == NULL) {
142#ifdef _JAVASOFT_JNI_H_ // Oracle's jni.h violates the JNI spec!
143 void* env;
144#else
145 JNIEnv* env;
146#endif
147 CHECK(!g_jvm->AttachCurrentThread(&env, NULL), "Failed to attach thread");
148 CHECK(env, "AttachCurrentThread handed back NULL!");
149 jni = reinterpret_cast<JNIEnv*>(env);
150 CHECK(!pthread_setspecific(g_jni_ptr, jni), "pthread_setspecific");
151 }
152 return jni;
153}
154
155// Android's FindClass() is trickier than usual because the app-specific
156// ClassLoader is not consulted when there is no app-specific frame on the
157// stack. Consequently, we only look up classes once in JNI_OnLoad.
158// http://developer.android.com/training/articles/perf-jni.html#faq_FindClass
159class ClassReferenceHolder {
160 public:
161 explicit ClassReferenceHolder(JNIEnv* jni) {
162 LoadClass(jni, "java/nio/ByteBuffer");
163 LoadClass(jni, "org/webrtc/AudioTrack");
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000164 LoadClass(jni, "org/webrtc/DataChannel");
165 LoadClass(jni, "org/webrtc/DataChannel$Buffer");
166 LoadClass(jni, "org/webrtc/DataChannel$Init");
167 LoadClass(jni, "org/webrtc/DataChannel$State");
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000168 LoadClass(jni, "org/webrtc/IceCandidate");
169 LoadClass(jni, "org/webrtc/MediaSource$State");
170 LoadClass(jni, "org/webrtc/MediaStream");
171 LoadClass(jni, "org/webrtc/MediaStreamTrack$State");
172 LoadClass(jni, "org/webrtc/PeerConnection$SignalingState");
173 LoadClass(jni, "org/webrtc/PeerConnection$IceConnectionState");
174 LoadClass(jni, "org/webrtc/PeerConnection$IceGatheringState");
175 LoadClass(jni, "org/webrtc/SessionDescription");
176 LoadClass(jni, "org/webrtc/SessionDescription$Type");
177 LoadClass(jni, "org/webrtc/StatsReport");
178 LoadClass(jni, "org/webrtc/StatsReport$Value");
179 LoadClass(jni, "org/webrtc/VideoRenderer$I420Frame");
180 LoadClass(jni, "org/webrtc/VideoTrack");
181 }
182
183 ~ClassReferenceHolder() {
184 CHECK(classes_.empty(), "Must call FreeReferences() before dtor!");
185 }
186
187 void FreeReferences(JNIEnv* jni) {
188 for (std::map<std::string, jclass>::const_iterator it = classes_.begin();
189 it != classes_.end(); ++it) {
190 jni->DeleteGlobalRef(it->second);
191 }
192 classes_.clear();
193 }
194
195 jclass GetClass(const std::string& name) {
196 std::map<std::string, jclass>::iterator it = classes_.find(name);
197 CHECK(it != classes_.end(), "Unexpected GetClass() call for: " << name);
198 return it->second;
199 }
200
201 private:
202 void LoadClass(JNIEnv* jni, const std::string& name) {
203 jclass localRef = jni->FindClass(name.c_str());
204 CHECK_EXCEPTION(jni, "error during FindClass: " << name);
205 CHECK(localRef, name);
206 jclass globalRef = reinterpret_cast<jclass>(jni->NewGlobalRef(localRef));
207 CHECK_EXCEPTION(jni, "error during NewGlobalRef: " << name);
208 CHECK(globalRef, name);
209 bool inserted = classes_.insert(std::make_pair(name, globalRef)).second;
210 CHECK(inserted, "Duplicate class name: " << name);
211 }
212
213 std::map<std::string, jclass> classes_;
214};
215
216// Allocated in JNI_OnLoad(), freed in JNI_OnUnLoad().
217static ClassReferenceHolder* g_class_reference_holder = NULL;
218
219// JNIEnv-helper methods that CHECK success: no Java exception thrown and found
220// object/class/method/field is non-null.
221jmethodID GetMethodID(
222 JNIEnv* jni, jclass c, const std::string& name, const char* signature) {
223 jmethodID m = jni->GetMethodID(c, name.c_str(), signature);
224 CHECK_EXCEPTION(jni,
225 "error during GetMethodID: " << name << ", " << signature);
226 CHECK(m, name << ", " << signature);
227 return m;
228}
229
230jmethodID GetStaticMethodID(
231 JNIEnv* jni, jclass c, const char* name, const char* signature) {
232 jmethodID m = jni->GetStaticMethodID(c, name, signature);
233 CHECK_EXCEPTION(jni,
234 "error during GetStaticMethodID: "
235 << name << ", " << signature);
236 CHECK(m, name << ", " << signature);
237 return m;
238}
239
240jfieldID GetFieldID(
241 JNIEnv* jni, jclass c, const char* name, const char* signature) {
242 jfieldID f = jni->GetFieldID(c, name, signature);
243 CHECK_EXCEPTION(jni, "error during GetFieldID");
244 CHECK(f, name << ", " << signature);
245 return f;
246}
247
248jclass FindClass(JNIEnv* jni, const char* name) {
249 return g_class_reference_holder->GetClass(name);
250}
251
252jclass GetObjectClass(JNIEnv* jni, jobject object) {
253 jclass c = jni->GetObjectClass(object);
254 CHECK_EXCEPTION(jni, "error during GetObjectClass");
255 CHECK(c, "");
256 return c;
257}
258
259jobject GetObjectField(JNIEnv* jni, jobject object, jfieldID id) {
260 jobject o = jni->GetObjectField(object, id);
261 CHECK_EXCEPTION(jni, "error during GetObjectField");
262 CHECK(o, "");
263 return o;
264}
265
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000266jstring GetStringField(JNIEnv* jni, jobject object, jfieldID id) {
267 return static_cast<jstring>(GetObjectField(jni, object, id));
268}
269
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000270jlong GetLongField(JNIEnv* jni, jobject object, jfieldID id) {
271 jlong l = jni->GetLongField(object, id);
272 CHECK_EXCEPTION(jni, "error during GetLongField");
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000273 return l;
274}
275
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000276jint GetIntField(JNIEnv* jni, jobject object, jfieldID id) {
277 jint i = jni->GetIntField(object, id);
278 CHECK_EXCEPTION(jni, "error during GetIntField");
279 return i;
280}
281
282bool GetBooleanField(JNIEnv* jni, jobject object, jfieldID id) {
283 jboolean b = jni->GetBooleanField(object, id);
284 CHECK_EXCEPTION(jni, "error during GetBooleanField");
285 return b;
286}
287
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000288jobject NewGlobalRef(JNIEnv* jni, jobject o) {
289 jobject ret = jni->NewGlobalRef(o);
290 CHECK_EXCEPTION(jni, "error during NewGlobalRef");
291 CHECK(ret, "");
292 return ret;
293}
294
295void DeleteGlobalRef(JNIEnv* jni, jobject o) {
296 jni->DeleteGlobalRef(o);
297 CHECK_EXCEPTION(jni, "error during DeleteGlobalRef");
298}
299
300// Given a jweak reference, allocate a (strong) local reference scoped to the
301// lifetime of this object if the weak reference is still valid, or NULL
302// otherwise.
303class WeakRef {
304 public:
305 WeakRef(JNIEnv* jni, jweak ref)
306 : jni_(jni), obj_(jni_->NewLocalRef(ref)) {
307 CHECK_EXCEPTION(jni, "error during NewLocalRef");
308 }
309 ~WeakRef() {
310 if (obj_) {
311 jni_->DeleteLocalRef(obj_);
312 CHECK_EXCEPTION(jni_, "error during DeleteLocalRef");
313 }
314 }
315 jobject obj() { return obj_; }
316
317 private:
318 JNIEnv* const jni_;
319 jobject const obj_;
320};
321
322// Given a local ref, take ownership of it and delete the ref when this goes out
323// of scope.
324template<class T> // T is jclass, jobject, jintArray, etc.
325class ScopedLocalRef {
326 public:
327 ScopedLocalRef(JNIEnv* jni, T obj)
328 : jni_(jni), obj_(obj) {}
329 ~ScopedLocalRef() {
330 jni_->DeleteLocalRef(obj_);
331 }
332 T operator*() const {
333 return obj_;
334 }
335 private:
336 JNIEnv* jni_;
337 T obj_;
338};
339
340// Scoped holder for global Java refs.
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000341template<class T> // T is jclass, jobject, jintArray, etc.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000342class ScopedGlobalRef {
343 public:
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000344 explicit ScopedGlobalRef(JNIEnv* jni, T obj)
345 : obj_(static_cast<T>(jni->NewGlobalRef(obj))) {}
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000346 ~ScopedGlobalRef() {
347 DeleteGlobalRef(AttachCurrentThreadIfNeeded(), obj_);
348 }
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000349 T operator*() const {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000350 return obj_;
351 }
352 private:
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000353 T obj_;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000354};
355
356// Return the (singleton) Java Enum object corresponding to |index|;
357// |state_class_fragment| is something like "MediaSource$State".
358jobject JavaEnumFromIndex(
359 JNIEnv* jni, const std::string& state_class_fragment, int index) {
360 std::string state_class_name = "org/webrtc/" + state_class_fragment;
361 jclass state_class = FindClass(jni, state_class_name.c_str());
362 jmethodID state_values_id = GetStaticMethodID(
363 jni, state_class, "values", ("()[L" + state_class_name + ";").c_str());
364 ScopedLocalRef<jobjectArray> state_values(
365 jni,
366 (jobjectArray)jni->CallStaticObjectMethod(state_class, state_values_id));
367 CHECK_EXCEPTION(jni, "error during CallStaticObjectMethod");
368 jobject ret = jni->GetObjectArrayElement(*state_values, index);
369 CHECK_EXCEPTION(jni, "error during GetObjectArrayElement");
370 return ret;
371}
372
373// Given a UTF-8 encoded |native| string return a new (UTF-16) jstring.
374static jstring JavaStringFromStdString(JNIEnv* jni, const std::string& native) {
375 UnicodeString ustr(UnicodeString::fromUTF8(native));
376 jstring jstr = jni->NewString(ustr.getBuffer(), ustr.length());
377 CHECK_EXCEPTION(jni, "error during NewString");
378 return jstr;
379}
380
381// Given a (UTF-16) jstring return a new UTF-8 native string.
382static std::string JavaToStdString(JNIEnv* jni, const jstring& j_string) {
383 const jchar* jchars = jni->GetStringChars(j_string, NULL);
384 CHECK_EXCEPTION(jni, "Error during GetStringChars");
385 UnicodeString ustr(jchars, jni->GetStringLength(j_string));
386 CHECK_EXCEPTION(jni, "Error during GetStringLength");
387 jni->ReleaseStringChars(j_string, jchars);
388 CHECK_EXCEPTION(jni, "Error during ReleaseStringChars");
389 std::string ret;
390 return ustr.toUTF8String(ret);
391}
392
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000393static DataChannelInit JavaDataChannelInitToNative(
394 JNIEnv* jni, jobject j_init) {
395 DataChannelInit init;
396
397 jclass j_init_class = FindClass(jni, "org/webrtc/DataChannel$Init");
398 jfieldID ordered_id = GetFieldID(jni, j_init_class, "ordered", "Z");
399 jfieldID max_retransmit_time_id =
400 GetFieldID(jni, j_init_class, "maxRetransmitTimeMs", "I");
401 jfieldID max_retransmits_id =
402 GetFieldID(jni, j_init_class, "maxRetransmits", "I");
403 jfieldID protocol_id =
404 GetFieldID(jni, j_init_class, "protocol", "Ljava/lang/String;");
405 jfieldID negotiated_id = GetFieldID(jni, j_init_class, "negotiated", "Z");
406 jfieldID id_id = GetFieldID(jni, j_init_class, "id", "I");
407
408 init.ordered = GetBooleanField(jni, j_init, ordered_id);
409 init.maxRetransmitTime = GetIntField(jni, j_init, max_retransmit_time_id);
410 init.maxRetransmits = GetIntField(jni, j_init, max_retransmits_id);
411 init.protocol = JavaToStdString(
412 jni, GetStringField(jni, j_init, protocol_id));
413 init.negotiated = GetBooleanField(jni, j_init, negotiated_id);
414 init.id = GetIntField(jni, j_init, id_id);
415
416 return init;
417}
418
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000419class ConstraintsWrapper;
420
421// Adapter between the C++ PeerConnectionObserver interface and the Java
422// PeerConnection.Observer interface. Wraps an instance of the Java interface
423// and dispatches C++ callbacks to Java.
424class PCOJava : public PeerConnectionObserver {
425 public:
426 PCOJava(JNIEnv* jni, jobject j_observer)
427 : j_observer_global_(jni, j_observer),
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000428 j_observer_class_(jni, GetObjectClass(jni, *j_observer_global_)),
429 j_media_stream_class_(jni, FindClass(jni, "org/webrtc/MediaStream")),
430 j_media_stream_ctor_(GetMethodID(
431 jni, *j_media_stream_class_, "<init>", "(J)V")),
432 j_audio_track_class_(jni, FindClass(jni, "org/webrtc/AudioTrack")),
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000433 j_audio_track_ctor_(GetMethodID(
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000434 jni, *j_audio_track_class_, "<init>", "(J)V")),
435 j_video_track_class_(jni, FindClass(jni, "org/webrtc/VideoTrack")),
436 j_video_track_ctor_(GetMethodID(
437 jni, *j_video_track_class_, "<init>", "(J)V")),
438 j_data_channel_class_(jni, FindClass(jni, "org/webrtc/DataChannel")),
439 j_data_channel_ctor_(GetMethodID(
440 jni, *j_data_channel_class_, "<init>", "(J)V")) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000441 }
442
443 virtual ~PCOJava() {}
444
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000445 virtual void OnIceCandidate(const IceCandidateInterface* candidate) OVERRIDE {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000446 std::string sdp;
447 CHECK(candidate->ToString(&sdp), "got so far: " << sdp);
448 jclass candidate_class = FindClass(jni(), "org/webrtc/IceCandidate");
449 jmethodID ctor = GetMethodID(jni(), candidate_class,
450 "<init>", "(Ljava/lang/String;ILjava/lang/String;)V");
451 ScopedLocalRef<jstring> j_mid(
452 jni(), JavaStringFromStdString(jni(), candidate->sdp_mid()));
453 ScopedLocalRef<jstring> j_sdp(jni(), JavaStringFromStdString(jni(), sdp));
454 ScopedLocalRef<jobject> j_candidate(jni(), jni()->NewObject(
455 candidate_class, ctor, *j_mid, candidate->sdp_mline_index(), *j_sdp));
456 CHECK_EXCEPTION(jni(), "error during NewObject");
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000457 jmethodID m = GetMethodID(jni(), *j_observer_class_,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000458 "onIceCandidate", "(Lorg/webrtc/IceCandidate;)V");
459 jni()->CallVoidMethod(*j_observer_global_, m, *j_candidate);
460 CHECK_EXCEPTION(jni(), "error during CallVoidMethod");
461 }
462
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000463 virtual void OnError() OVERRIDE {
464 jmethodID m = GetMethodID(jni(), *j_observer_class_, "onError", "(V)V");
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000465 jni()->CallVoidMethod(*j_observer_global_, m);
466 CHECK_EXCEPTION(jni(), "error during CallVoidMethod");
467 }
468
469 virtual void OnSignalingChange(
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000470 PeerConnectionInterface::SignalingState new_state) OVERRIDE {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000471 jmethodID m = GetMethodID(
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000472 jni(), *j_observer_class_, "onSignalingChange",
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000473 "(Lorg/webrtc/PeerConnection$SignalingState;)V");
474 ScopedLocalRef<jobject> new_state_enum(jni(), JavaEnumFromIndex(
475 jni(), "PeerConnection$SignalingState", new_state));
476 jni()->CallVoidMethod(*j_observer_global_, m, *new_state_enum);
477 CHECK_EXCEPTION(jni(), "error during CallVoidMethod");
478 }
479
480 virtual void OnIceConnectionChange(
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000481 PeerConnectionInterface::IceConnectionState new_state) OVERRIDE {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000482 jmethodID m = GetMethodID(
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000483 jni(), *j_observer_class_, "onIceConnectionChange",
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000484 "(Lorg/webrtc/PeerConnection$IceConnectionState;)V");
485 ScopedLocalRef<jobject> new_state_enum(jni(), JavaEnumFromIndex(
486 jni(), "PeerConnection$IceConnectionState", new_state));
487 jni()->CallVoidMethod(*j_observer_global_, m, *new_state_enum);
488 CHECK_EXCEPTION(jni(), "error during CallVoidMethod");
489 }
490
491 virtual void OnIceGatheringChange(
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000492 PeerConnectionInterface::IceGatheringState new_state) OVERRIDE {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000493 jmethodID m = GetMethodID(
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000494 jni(), *j_observer_class_, "onIceGatheringChange",
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000495 "(Lorg/webrtc/PeerConnection$IceGatheringState;)V");
496 ScopedLocalRef<jobject> new_state_enum(jni(), JavaEnumFromIndex(
497 jni(), "PeerConnection$IceGatheringState", new_state));
498 jni()->CallVoidMethod(*j_observer_global_, m, *new_state_enum);
499 CHECK_EXCEPTION(jni(), "error during CallVoidMethod");
500 }
501
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000502 virtual void OnAddStream(MediaStreamInterface* stream) OVERRIDE {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000503 ScopedLocalRef<jobject> j_stream(jni(), jni()->NewObject(
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000504 *j_media_stream_class_, j_media_stream_ctor_, (jlong)stream));
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000505 CHECK_EXCEPTION(jni(), "error during NewObject");
506
507 AudioTrackVector audio_tracks = stream->GetAudioTracks();
508 for (size_t i = 0; i < audio_tracks.size(); ++i) {
509 AudioTrackInterface* track = audio_tracks[i];
510 ScopedLocalRef<jstring> id(
511 jni(), JavaStringFromStdString(jni(), track->id()));
512 ScopedLocalRef<jobject> j_track(jni(), jni()->NewObject(
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000513 *j_audio_track_class_, j_audio_track_ctor_, (jlong)track, *id));
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000514 CHECK_EXCEPTION(jni(), "error during NewObject");
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000515 jfieldID audio_tracks_id = GetFieldID(
516 jni(), *j_media_stream_class_, "audioTracks", "Ljava/util/List;");
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000517 ScopedLocalRef<jobject> audio_tracks(jni(), GetObjectField(
518 jni(), *j_stream, audio_tracks_id));
519 jmethodID add = GetMethodID(jni(),
520 GetObjectClass(jni(), *audio_tracks), "add", "(Ljava/lang/Object;)Z");
521 jboolean added = jni()->CallBooleanMethod(*audio_tracks, add, *j_track);
522 CHECK_EXCEPTION(jni(), "error during CallBooleanMethod");
523 CHECK(added, "");
524 }
525
526 VideoTrackVector video_tracks = stream->GetVideoTracks();
527 for (size_t i = 0; i < video_tracks.size(); ++i) {
528 VideoTrackInterface* track = video_tracks[i];
529 ScopedLocalRef<jstring> id(
530 jni(), JavaStringFromStdString(jni(), track->id()));
531 ScopedLocalRef<jobject> j_track(jni(), jni()->NewObject(
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000532 *j_video_track_class_, j_video_track_ctor_, (jlong)track, *id));
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000533 CHECK_EXCEPTION(jni(), "error during NewObject");
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000534 jfieldID video_tracks_id = GetFieldID(
535 jni(), *j_media_stream_class_, "videoTracks", "Ljava/util/List;");
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000536 ScopedLocalRef<jobject> video_tracks(jni(), GetObjectField(
537 jni(), *j_stream, video_tracks_id));
538 jmethodID add = GetMethodID(jni(),
539 GetObjectClass(jni(), *video_tracks), "add", "(Ljava/lang/Object;)Z");
540 jboolean added = jni()->CallBooleanMethod(*video_tracks, add, *j_track);
541 CHECK_EXCEPTION(jni(), "error during CallBooleanMethod");
542 CHECK(added, "");
543 }
544 streams_[stream] = jni()->NewWeakGlobalRef(*j_stream);
545 CHECK_EXCEPTION(jni(), "error during NewWeakGlobalRef");
546
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000547 jmethodID m = GetMethodID(jni(), *j_observer_class_, "onAddStream",
548 "(Lorg/webrtc/MediaStream;)V");
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000549 jni()->CallVoidMethod(*j_observer_global_, m, *j_stream);
550 CHECK_EXCEPTION(jni(), "error during CallVoidMethod");
551 }
552
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000553 virtual void OnRemoveStream(MediaStreamInterface* stream) OVERRIDE {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000554 NativeToJavaStreamsMap::iterator it = streams_.find(stream);
555 CHECK(it != streams_.end(), "unexpected stream: " << std::hex << stream);
556
557 WeakRef s(jni(), it->second);
558 streams_.erase(it);
559 if (!s.obj())
560 return;
561
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000562 jmethodID m = GetMethodID(jni(), *j_observer_class_, "onRemoveStream",
563 "(Lorg/webrtc/MediaStream;)V");
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000564 jni()->CallVoidMethod(*j_observer_global_, m, s.obj());
565 CHECK_EXCEPTION(jni(), "error during CallVoidMethod");
566 }
567
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000568 virtual void OnDataChannel(DataChannelInterface* channel) OVERRIDE {
569 ScopedLocalRef<jobject> j_channel(jni(), jni()->NewObject(
570 *j_data_channel_class_, j_data_channel_ctor_, (jlong)channel));
571 CHECK_EXCEPTION(jni(), "error during NewObject");
572 // Channel is now owned by Java object, and will be freed from
573 // DataChannel.dispose().
574 channel->AddRef();
575
576 jmethodID m = GetMethodID(jni(), *j_observer_class_, "onDataChannel",
577 "(Lorg/webrtc/DataChannel;)V");
578 jni()->CallVoidMethod(*j_observer_global_, m, *j_channel);
579 CHECK_EXCEPTION(jni(), "error during CallVoidMethod");
580 }
581
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000582 void SetConstraints(ConstraintsWrapper* constraints) {
583 CHECK(!constraints_.get(), "constraints already set!");
584 constraints_.reset(constraints);
585 }
586
587 const ConstraintsWrapper* constraints() { return constraints_.get(); }
588
589 private:
590 JNIEnv* jni() {
591 return AttachCurrentThreadIfNeeded();
592 }
593
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000594 const ScopedGlobalRef<jobject> j_observer_global_;
595 const ScopedGlobalRef<jclass> j_observer_class_;
596 const ScopedGlobalRef<jclass> j_media_stream_class_;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000597 const jmethodID j_media_stream_ctor_;
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000598 const ScopedGlobalRef<jclass> j_audio_track_class_;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000599 const jmethodID j_audio_track_ctor_;
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000600 const ScopedGlobalRef<jclass> j_video_track_class_;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000601 const jmethodID j_video_track_ctor_;
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000602 const ScopedGlobalRef<jclass> j_data_channel_class_;
603 const jmethodID j_data_channel_ctor_;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000604 typedef std::map<void*, jweak> NativeToJavaStreamsMap;
605 NativeToJavaStreamsMap streams_; // C++ -> Java streams.
606 talk_base::scoped_ptr<ConstraintsWrapper> constraints_;
607};
608
609// Wrapper for a Java MediaConstraints object. Copies all needed data so when
610// the constructor returns the Java object is no longer needed.
611class ConstraintsWrapper : public MediaConstraintsInterface {
612 public:
613 ConstraintsWrapper(JNIEnv* jni, jobject j_constraints) {
614 PopulateConstraintsFromJavaPairList(
615 jni, j_constraints, "mandatory", &mandatory_);
616 PopulateConstraintsFromJavaPairList(
617 jni, j_constraints, "optional", &optional_);
618 }
619
620 virtual ~ConstraintsWrapper() {}
621
622 // MediaConstraintsInterface.
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000623 virtual const Constraints& GetMandatory() const OVERRIDE {
624 return mandatory_;
625 }
626
627 virtual const Constraints& GetOptional() const OVERRIDE {
628 return optional_;
629 }
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000630
631 private:
632 // Helper for translating a List<Pair<String, String>> to a Constraints.
633 static void PopulateConstraintsFromJavaPairList(
634 JNIEnv* jni, jobject j_constraints,
635 const char* field_name, Constraints* field) {
636 jfieldID j_id = GetFieldID(jni,
637 GetObjectClass(jni, j_constraints), field_name, "Ljava/util/List;");
638 jobject j_list = GetObjectField(jni, j_constraints, j_id);
639 jmethodID j_iterator_id = GetMethodID(jni,
640 GetObjectClass(jni, j_list), "iterator", "()Ljava/util/Iterator;");
641 jobject j_iterator = jni->CallObjectMethod(j_list, j_iterator_id);
642 CHECK_EXCEPTION(jni, "error during CallObjectMethod");
643 jmethodID j_has_next = GetMethodID(jni,
644 GetObjectClass(jni, j_iterator), "hasNext", "()Z");
645 jmethodID j_next = GetMethodID(jni,
646 GetObjectClass(jni, j_iterator), "next", "()Ljava/lang/Object;");
647 while (jni->CallBooleanMethod(j_iterator, j_has_next)) {
648 CHECK_EXCEPTION(jni, "error during CallBooleanMethod");
649 jobject entry = jni->CallObjectMethod(j_iterator, j_next);
650 CHECK_EXCEPTION(jni, "error during CallObjectMethod");
651 jmethodID get_key = GetMethodID(jni,
652 GetObjectClass(jni, entry), "getKey", "()Ljava/lang/String;");
653 jstring j_key = reinterpret_cast<jstring>(
654 jni->CallObjectMethod(entry, get_key));
655 CHECK_EXCEPTION(jni, "error during CallObjectMethod");
656 jmethodID get_value = GetMethodID(jni,
657 GetObjectClass(jni, entry), "getValue", "()Ljava/lang/String;");
658 jstring j_value = reinterpret_cast<jstring>(
659 jni->CallObjectMethod(entry, get_value));
660 CHECK_EXCEPTION(jni, "error during CallObjectMethod");
661 field->push_back(Constraint(JavaToStdString(jni, j_key),
662 JavaToStdString(jni, j_value)));
663 }
664 CHECK_EXCEPTION(jni, "error during CallBooleanMethod");
665 }
666
667 Constraints mandatory_;
668 Constraints optional_;
669};
670
671static jobject JavaSdpFromNativeSdp(
672 JNIEnv* jni, const SessionDescriptionInterface* desc) {
673 std::string sdp;
674 CHECK(desc->ToString(&sdp), "got so far: " << sdp);
675 ScopedLocalRef<jstring> j_description(jni, JavaStringFromStdString(jni, sdp));
676
677 jclass j_type_class = FindClass(
678 jni, "org/webrtc/SessionDescription$Type");
679 jmethodID j_type_from_canonical = GetStaticMethodID(
680 jni, j_type_class, "fromCanonicalForm",
681 "(Ljava/lang/String;)Lorg/webrtc/SessionDescription$Type;");
682 ScopedLocalRef<jstring> j_type_string(
683 jni, JavaStringFromStdString(jni, desc->type()));
684 jobject j_type = jni->CallStaticObjectMethod(
685 j_type_class, j_type_from_canonical, *j_type_string);
686 CHECK_EXCEPTION(jni, "error during CallObjectMethod");
687
688 jclass j_sdp_class = FindClass(jni, "org/webrtc/SessionDescription");
689 jmethodID j_sdp_ctor = GetMethodID(
690 jni, j_sdp_class, "<init>",
691 "(Lorg/webrtc/SessionDescription$Type;Ljava/lang/String;)V");
692 jobject j_sdp = jni->NewObject(
693 j_sdp_class, j_sdp_ctor, j_type, *j_description);
694 CHECK_EXCEPTION(jni, "error during NewObject");
695 return j_sdp;
696}
697
698template <class T> // T is one of {Create,Set}SessionDescriptionObserver.
699class SdpObserverWrapper : public T {
700 public:
701 SdpObserverWrapper(JNIEnv* jni, jobject j_observer,
702 ConstraintsWrapper* constraints)
703 : constraints_(constraints),
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000704 j_observer_global_(jni, j_observer),
705 j_observer_class_(jni, GetObjectClass(jni, j_observer)) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000706 }
707
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000708 virtual ~SdpObserverWrapper() {}
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000709
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000710 // Can't mark OVERRIDE because of templating.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000711 virtual void OnSuccess() {
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000712 jmethodID m = GetMethodID(jni(), *j_observer_class_, "onSetSuccess", "()V");
713 jni()->CallVoidMethod(*j_observer_global_, m);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000714 CHECK_EXCEPTION(jni(), "error during CallVoidMethod");
715 }
716
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000717 // Can't mark OVERRIDE because of templating.
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000718 virtual void OnSuccess(SessionDescriptionInterface* desc) {
719 jmethodID m = GetMethodID(
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000720 jni(), *j_observer_class_, "onCreateSuccess",
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000721 "(Lorg/webrtc/SessionDescription;)V");
722 ScopedLocalRef<jobject> j_sdp(jni(), JavaSdpFromNativeSdp(jni(), desc));
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000723 jni()->CallVoidMethod(*j_observer_global_, m, *j_sdp);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000724 CHECK_EXCEPTION(jni(), "error during CallVoidMethod");
725 }
726
727 protected:
728 // Common implementation for failure of Set & Create types, distinguished by
729 // |op| being "Set" or "Create".
730 void OnFailure(const std::string& op, const std::string& error) {
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000731 jmethodID m = GetMethodID(jni(), *j_observer_class_, "on" + op + "Failure",
732 "(Ljava/lang/String;)V");
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000733 ScopedLocalRef<jstring> j_error_string(
734 jni(), JavaStringFromStdString(jni(), error));
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000735 jni()->CallVoidMethod(*j_observer_global_, m, *j_error_string);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000736 CHECK_EXCEPTION(jni(), "error during CallVoidMethod");
737 }
738
739 private:
740 JNIEnv* jni() {
741 return AttachCurrentThreadIfNeeded();
742 }
743
744 talk_base::scoped_ptr<ConstraintsWrapper> constraints_;
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000745 const ScopedGlobalRef<jobject> j_observer_global_;
746 const ScopedGlobalRef<jclass> j_observer_class_;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000747};
748
749class CreateSdpObserverWrapper
750 : public SdpObserverWrapper<CreateSessionDescriptionObserver> {
751 public:
752 CreateSdpObserverWrapper(JNIEnv* jni, jobject j_observer,
753 ConstraintsWrapper* constraints)
754 : SdpObserverWrapper(jni, j_observer, constraints) {}
755
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000756 virtual void OnFailure(const std::string& error) OVERRIDE {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000757 SdpObserverWrapper::OnFailure(std::string("Create"), error);
758 }
759};
760
761class SetSdpObserverWrapper
762 : public SdpObserverWrapper<SetSessionDescriptionObserver> {
763 public:
764 SetSdpObserverWrapper(JNIEnv* jni, jobject j_observer,
765 ConstraintsWrapper* constraints)
766 : SdpObserverWrapper(jni, j_observer, constraints) {}
767
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000768 virtual void OnFailure(const std::string& error) OVERRIDE {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000769 SdpObserverWrapper::OnFailure(std::string("Set"), error);
770 }
771};
772
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000773// Adapter for a Java DataChannel$Observer presenting a C++ DataChannelObserver
774// and dispatching the callback from C++ back to Java.
775class DataChannelObserverWrapper : public DataChannelObserver {
776 public:
777 DataChannelObserverWrapper(JNIEnv* jni, jobject j_observer)
778 : j_observer_global_(jni, j_observer),
779 j_observer_class_(jni, GetObjectClass(jni, j_observer)),
780 j_on_state_change_mid_(GetMethodID(jni, *j_observer_class_,
781 "onStateChange", "()V")),
782 j_on_message_mid_(GetMethodID(jni, *j_observer_class_, "onMessage",
783 "(Lorg/webrtc/DataChannel$Buffer;)V")),
784 j_buffer_class_(jni, FindClass(jni, "org/webrtc/DataChannel$Buffer")),
785 j_buffer_ctor_(GetMethodID(jni, *j_buffer_class_,
786 "<init>", "(Ljava/nio/ByteBuffer;Z)V")) {
787 }
788
789 virtual ~DataChannelObserverWrapper() {}
790
791 virtual void OnStateChange() OVERRIDE {
792 jni()->CallVoidMethod(*j_observer_global_, j_on_state_change_mid_);
793 CHECK_EXCEPTION(jni(), "error during CallVoidMethod");
794 }
795
796 virtual void OnMessage(const DataBuffer& buffer) OVERRIDE {
797 jobject byte_buffer =
798 jni()->NewDirectByteBuffer(const_cast<char*>(buffer.data.data()),
799 buffer.data.length());
800 jobject j_buffer = jni()->NewObject(*j_buffer_class_, j_buffer_ctor_,
801 byte_buffer, buffer.binary);
802 jni()->CallVoidMethod(*j_observer_global_, j_on_message_mid_, j_buffer);
803 CHECK_EXCEPTION(jni(), "error during CallVoidMethod");
804 }
805
806 private:
807 JNIEnv* jni() {
808 return AttachCurrentThreadIfNeeded();
809 }
810
811 const ScopedGlobalRef<jobject> j_observer_global_;
812 const ScopedGlobalRef<jclass> j_observer_class_;
813 const ScopedGlobalRef<jclass> j_buffer_class_;
814 const jmethodID j_on_state_change_mid_;
815 const jmethodID j_on_message_mid_;
816 const jmethodID j_buffer_ctor_;
817};
818
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000819// Adapter for a Java StatsObserver presenting a C++ StatsObserver and
820// dispatching the callback from C++ back to Java.
821class StatsObserverWrapper : public StatsObserver {
822 public:
823 StatsObserverWrapper(JNIEnv* jni, jobject j_observer)
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000824 : j_observer_global_(jni, j_observer),
825 j_observer_class_(jni, GetObjectClass(jni, j_observer)),
826 j_stats_report_class_(jni, FindClass(jni, "org/webrtc/StatsReport")),
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000827 j_stats_report_ctor_(GetMethodID(
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000828 jni, *j_stats_report_class_, "<init>",
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000829 "(Ljava/lang/String;Ljava/lang/String;D"
830 "[Lorg/webrtc/StatsReport$Value;)V")),
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000831 j_value_class_(jni, FindClass(
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000832 jni, "org/webrtc/StatsReport$Value")),
833 j_value_ctor_(GetMethodID(
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000834 jni, *j_value_class_, "<init>",
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000835 "(Ljava/lang/String;Ljava/lang/String;)V")) {
836 }
837
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000838 virtual ~StatsObserverWrapper() {}
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000839
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000840 virtual void OnComplete(const std::vector<StatsReport>& reports) OVERRIDE {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000841 ScopedLocalRef<jobjectArray> j_reports(jni(),
842 ReportsToJava(jni(), reports));
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000843 jmethodID m = GetMethodID(jni(), *j_observer_class_, "onComplete",
844 "([Lorg/webrtc/StatsReport;)V");
845 jni()->CallVoidMethod(*j_observer_global_, m, *j_reports);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000846 CHECK_EXCEPTION(jni(), "error during CallVoidMethod");
847 }
848
849 private:
850 jobjectArray ReportsToJava(
851 JNIEnv* jni, const std::vector<StatsReport>& reports) {
852 jobjectArray reports_array = jni->NewObjectArray(
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000853 reports.size(), *j_stats_report_class_, NULL);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000854 for (int i = 0; i < reports.size(); ++i) {
855 const StatsReport& report = reports[i];
856 ScopedLocalRef<jstring> j_id(
857 jni, JavaStringFromStdString(jni, report.id));
858 ScopedLocalRef<jstring> j_type(
859 jni, JavaStringFromStdString(jni, report.type));
860 ScopedLocalRef<jobjectArray> j_values(
861 jni, ValuesToJava(jni, report.values));
862 ScopedLocalRef<jobject> j_report(jni, jni->NewObject(
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000863 *j_stats_report_class_, j_stats_report_ctor_, *j_id, *j_type,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000864 report.timestamp, *j_values));
865 jni->SetObjectArrayElement(reports_array, i, *j_report);
866 }
867 return reports_array;
868 }
869
870 jobjectArray ValuesToJava(JNIEnv* jni, const StatsReport::Values& values) {
871 jobjectArray j_values = jni->NewObjectArray(
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000872 values.size(), *j_value_class_, NULL);
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000873 for (int i = 0; i < values.size(); ++i) {
874 const StatsReport::Value& value = values[i];
875 ScopedLocalRef<jstring> j_name(
876 jni, JavaStringFromStdString(jni, value.name));
877 ScopedLocalRef<jstring> j_value(
878 jni, JavaStringFromStdString(jni, value.value));
879 ScopedLocalRef<jobject> j_element_value(jni, jni->NewObject(
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000880 *j_value_class_, j_value_ctor_, *j_name, *j_value));
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000881 jni->SetObjectArrayElement(j_values, i, *j_element_value);
882 }
883 return j_values;
884 }
885
886 JNIEnv* jni() {
887 return AttachCurrentThreadIfNeeded();
888 }
889
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000890 const ScopedGlobalRef<jobject> j_observer_global_;
891 const ScopedGlobalRef<jclass> j_observer_class_;
892 const ScopedGlobalRef<jclass> j_stats_report_class_;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000893 const jmethodID j_stats_report_ctor_;
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000894 const ScopedGlobalRef<jclass> j_value_class_;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000895 const jmethodID j_value_ctor_;
896};
897
898// Adapter presenting a cricket::VideoRenderer as a
899// webrtc::VideoRendererInterface.
900class VideoRendererWrapper : public VideoRendererInterface {
901 public:
902 static VideoRendererWrapper* Create(cricket::VideoRenderer* renderer) {
903 if (renderer)
904 return new VideoRendererWrapper(renderer);
905 return NULL;
906 }
907
908 virtual ~VideoRendererWrapper() {}
909
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000910 virtual void SetSize(int width, int height) OVERRIDE {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000911 const bool kNotReserved = false; // What does this param mean??
912 renderer_->SetSize(width, height, kNotReserved);
913 }
914
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000915 virtual void RenderFrame(const cricket::VideoFrame* frame) OVERRIDE {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000916 renderer_->RenderFrame(frame);
917 }
918
919 private:
920 explicit VideoRendererWrapper(cricket::VideoRenderer* renderer)
921 : renderer_(renderer) {}
922
923 talk_base::scoped_ptr<cricket::VideoRenderer> renderer_;
924};
925
926// Wrapper dispatching webrtc::VideoRendererInterface to a Java VideoRenderer
927// instance.
928class JavaVideoRendererWrapper : public VideoRendererInterface {
929 public:
930 JavaVideoRendererWrapper(JNIEnv* jni, jobject j_callbacks)
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000931 : j_callbacks_(jni, j_callbacks),
932 j_set_size_id_(GetMethodID(
933 jni, GetObjectClass(jni, j_callbacks), "setSize", "(II)V")),
934 j_render_frame_id_(GetMethodID(
935 jni, GetObjectClass(jni, j_callbacks), "renderFrame",
936 "(Lorg/webrtc/VideoRenderer$I420Frame;)V")),
937 j_frame_class_(jni,
938 FindClass(jni, "org/webrtc/VideoRenderer$I420Frame")),
939 j_frame_ctor_id_(GetMethodID(
940 jni, *j_frame_class_, "<init>", "(II[I[Ljava/nio/ByteBuffer;)V")),
941 j_byte_buffer_class_(jni, FindClass(jni, "java/nio/ByteBuffer")) {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000942 CHECK_EXCEPTION(jni, "");
943 }
944
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000945 virtual ~JavaVideoRendererWrapper() {}
946
947 virtual void SetSize(int width, int height) OVERRIDE {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000948 jni()->CallVoidMethod(*j_callbacks_, j_set_size_id_, width, height);
949 CHECK_EXCEPTION(jni(), "");
950 }
951
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000952 virtual void RenderFrame(const cricket::VideoFrame* frame) OVERRIDE {
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000953 ScopedLocalRef<jobject> j_frame(jni(), CricketToJavaFrame(frame));
954 jni()->CallVoidMethod(*j_callbacks_, j_render_frame_id_, *j_frame);
955 CHECK_EXCEPTION(jni(), "");
956 }
957
958 private:
959 // Return a VideoRenderer.I420Frame referring to the data in |frame|.
960 jobject CricketToJavaFrame(const cricket::VideoFrame* frame) {
961 ScopedLocalRef<jintArray> strides(jni(), jni()->NewIntArray(3));
962 jint* strides_array = jni()->GetIntArrayElements(*strides, NULL);
963 strides_array[0] = frame->GetYPitch();
964 strides_array[1] = frame->GetUPitch();
965 strides_array[2] = frame->GetVPitch();
966 jni()->ReleaseIntArrayElements(*strides, strides_array, 0);
967 ScopedLocalRef<jobjectArray> planes(
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000968 jni(), jni()->NewObjectArray(3, *j_byte_buffer_class_, NULL));
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000969 ScopedLocalRef<jobject> y_buffer(jni(), jni()->NewDirectByteBuffer(
970 const_cast<uint8*>(frame->GetYPlane()),
971 frame->GetYPitch() * frame->GetHeight()));
972 ScopedLocalRef<jobject> u_buffer(jni(), jni()->NewDirectByteBuffer(
973 const_cast<uint8*>(frame->GetUPlane()), frame->GetChromaSize()));
974 ScopedLocalRef<jobject> v_buffer(jni(), jni()->NewDirectByteBuffer(
975 const_cast<uint8*>(frame->GetVPlane()), frame->GetChromaSize()));
976 jni()->SetObjectArrayElement(*planes, 0, *y_buffer);
977 jni()->SetObjectArrayElement(*planes, 1, *u_buffer);
978 jni()->SetObjectArrayElement(*planes, 2, *v_buffer);
979 return jni()->NewObject(
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000980 *j_frame_class_, j_frame_ctor_id_,
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000981 frame->GetWidth(), frame->GetHeight(), *strides, *planes);
982 }
983
984 JNIEnv* jni() {
985 return AttachCurrentThreadIfNeeded();
986 }
987
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000988 ScopedGlobalRef<jobject> j_callbacks_;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000989 jmethodID j_set_size_id_;
990 jmethodID j_render_frame_id_;
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000991 ScopedGlobalRef<jclass> j_frame_class_;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000992 jmethodID j_frame_ctor_id_;
henrike@webrtc.org723d6832013-07-12 16:04:50 +0000993 ScopedGlobalRef<jclass> j_byte_buffer_class_;
henrike@webrtc.org28e20752013-07-10 00:45:36 +0000994};
995
996} // anonymous namespace
997
998
999// Convenience macro defining JNI-accessible methods in the org.webrtc package.
1000// Eliminates unnecessary boilerplate and line-wraps, reducing visual clutter.
1001#define JOW(rettype, name) extern "C" rettype JNIEXPORT JNICALL \
1002 Java_org_webrtc_##name
1003
1004extern "C" jint JNIEXPORT JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved) {
1005 CHECK(!g_jvm, "JNI_OnLoad called more than once!");
1006 g_jvm = jvm;
1007 CHECK(g_jvm, "JNI_OnLoad handed NULL?");
1008
1009 CHECK(talk_base::InitializeSSL(), "Failed to InitializeSSL()");
1010
1011 JNIEnv* jni;
1012 if (jvm->GetEnv(reinterpret_cast<void**>(&jni), JNI_VERSION_1_6) != JNI_OK)
1013 return -1;
1014 g_class_reference_holder = new ClassReferenceHolder(jni);
1015
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001016 webrtc::Trace::CreateTrace();
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001017
1018 return JNI_VERSION_1_6;
1019}
1020
1021extern "C" void JNIEXPORT JNICALL JNI_OnUnLoad(JavaVM *jvm, void *reserved) {
1022 webrtc::Trace::ReturnTrace();
1023 delete g_class_reference_holder;
1024 g_class_reference_holder = NULL;
1025 CHECK(talk_base::CleanupSSL(), "Failed to CleanupSSL()");
1026}
1027
henrike@webrtc.org723d6832013-07-12 16:04:50 +00001028static talk_base::scoped_refptr<DataChannelInterface> ExtractNativeDC(
1029 JNIEnv* jni, jobject j_dc) {
1030 jfieldID native_dc_id = GetFieldID(jni,
1031 GetObjectClass(jni, j_dc), "nativeDataChannel", "J");
1032 jlong j_d = GetLongField(jni, j_dc, native_dc_id);
1033 return talk_base::scoped_refptr<DataChannelInterface>(
1034 reinterpret_cast<DataChannelInterface*>(j_d));
1035}
1036
1037JOW(jlong, DataChannel_registerObserverNative)(
1038 JNIEnv* jni, jobject j_dc, jobject j_observer) {
1039 talk_base::scoped_ptr<DataChannelObserverWrapper> observer(
1040 new DataChannelObserverWrapper(jni, j_observer));
1041 ExtractNativeDC(jni, j_dc)->RegisterObserver(observer.get());
1042 return reinterpret_cast<jlong>(observer.release());
1043}
1044
1045JOW(void, DataChannel_unregisterObserverNative)(
1046 JNIEnv* jni, jobject j_dc, jlong native_observer) {
1047 ExtractNativeDC(jni, j_dc)->UnregisterObserver();
1048 delete reinterpret_cast<DataChannelObserverWrapper*>(native_observer);
1049}
1050
1051JOW(jstring, DataChannel_label)(JNIEnv* jni, jobject j_dc) {
1052 return JavaStringFromStdString(jni, ExtractNativeDC(jni, j_dc)->label());
1053}
1054
1055JOW(jobject, DataChannel_state)(JNIEnv* jni, jobject j_dc) {
1056 return JavaEnumFromIndex(
1057 jni, "DataChannel$State", ExtractNativeDC(jni, j_dc)->state());
1058}
1059
1060JOW(jlong, DataChannel_bufferedAmount)(JNIEnv* jni, jobject j_dc) {
1061 uint64 buffered_amount = ExtractNativeDC(jni, j_dc)->buffered_amount();
1062 CHECK(buffered_amount <= std::numeric_limits<int64>::max(),
1063 "buffered_amount overflowed jlong!");
1064 return static_cast<jlong>(buffered_amount);
1065}
1066
1067JOW(void, DataChannel_close)(JNIEnv* jni, jobject j_dc) {
1068 ExtractNativeDC(jni, j_dc)->Close();
1069}
1070
1071JOW(jboolean, DataChannel_sendNative)(JNIEnv* jni, jobject j_dc,
1072 jbyteArray data, jboolean binary) {
1073 jbyte* bytes = jni->GetByteArrayElements(data, NULL);
1074 bool ret = ExtractNativeDC(jni, j_dc)->Send(DataBuffer(
1075 talk_base::Buffer(bytes, jni->GetArrayLength(data)),
1076 binary));
1077 jni->ReleaseByteArrayElements(data, bytes, JNI_ABORT);
1078 return ret;
1079}
1080
1081JOW(void, DataChannel_dispose)(JNIEnv* jni, jobject j_dc) {
1082 ExtractNativeDC(jni, j_dc)->Release();
1083}
1084
fischman@webrtc.orgc883fdc2013-08-06 19:00:53 +00001085JOW(void, Logging_nativeEnableTracing)(
1086 JNIEnv* jni, jclass, jstring j_path, jint nativeLevels,
1087 jint nativeSeverity) {
1088 std::string path = JavaToStdString(jni, j_path);
1089 if (nativeLevels != webrtc::kTraceNone) {
1090 CHECK(!webrtc::Trace::SetTraceFile(path.c_str(), false),
1091 "SetTraceFile failed");
1092 CHECK(!webrtc::Trace::SetLevelFilter(nativeLevels),
1093 "SetLevelFilter failed");
1094 }
1095 talk_base::LogMessage::LogToDebug(nativeSeverity);
1096}
1097
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001098JOW(void, PeerConnection_freePeerConnection)(JNIEnv*, jclass, jlong j_p) {
1099 reinterpret_cast<PeerConnectionInterface*>(j_p)->Release();
1100}
1101
1102JOW(void, PeerConnection_freeObserver)(JNIEnv*, jclass, jlong j_p) {
1103 PCOJava* p = reinterpret_cast<PCOJava*>(j_p);
1104 delete p;
1105}
1106
1107JOW(void, MediaSource_free)(JNIEnv*, jclass, jlong j_p) {
1108 reinterpret_cast<MediaSourceInterface*>(j_p)->Release();
1109}
1110
1111JOW(void, VideoCapturer_free)(JNIEnv*, jclass, jlong j_p) {
1112 delete reinterpret_cast<cricket::VideoCapturer*>(j_p);
1113}
1114
1115JOW(void, VideoRenderer_free)(JNIEnv*, jclass, jlong j_p) {
1116 delete reinterpret_cast<VideoRendererWrapper*>(j_p);
1117}
1118
1119JOW(void, MediaStreamTrack_free)(JNIEnv*, jclass, jlong j_p) {
1120 reinterpret_cast<MediaStreamTrackInterface*>(j_p)->Release();
1121}
1122
1123JOW(jboolean, MediaStream_nativeAddAudioTrack)(
1124 JNIEnv* jni, jclass, jlong pointer, jlong j_audio_track_pointer) {
1125 talk_base::scoped_refptr<MediaStreamInterface> stream(
1126 reinterpret_cast<MediaStreamInterface*>(pointer));
1127 talk_base::scoped_refptr<AudioTrackInterface> track(
1128 reinterpret_cast<AudioTrackInterface*>(j_audio_track_pointer));
1129 return stream->AddTrack(track);
1130}
1131
1132JOW(jboolean, MediaStream_nativeAddVideoTrack)(
1133 JNIEnv* jni, jclass, jlong pointer, jlong j_video_track_pointer) {
1134 talk_base::scoped_refptr<MediaStreamInterface> stream(
1135 reinterpret_cast<MediaStreamInterface*>(pointer));
1136 talk_base::scoped_refptr<VideoTrackInterface> track(
1137 reinterpret_cast<VideoTrackInterface*>(j_video_track_pointer));
1138 return stream->AddTrack(track);
1139}
1140
1141JOW(jboolean, MediaStream_nativeRemoveAudioTrack)(
1142 JNIEnv* jni, jclass, jlong pointer, jlong j_audio_track_pointer) {
1143 talk_base::scoped_refptr<MediaStreamInterface> stream(
1144 reinterpret_cast<MediaStreamInterface*>(pointer));
1145 talk_base::scoped_refptr<AudioTrackInterface> track(
1146 reinterpret_cast<AudioTrackInterface*>(j_audio_track_pointer));
1147 return stream->RemoveTrack(track);
1148}
1149
1150JOW(jboolean, MediaStream_nativeRemoveVideoTrack)(
1151 JNIEnv* jni, jclass, jlong pointer, jlong j_video_track_pointer) {
1152 talk_base::scoped_refptr<MediaStreamInterface> stream(
1153 reinterpret_cast<MediaStreamInterface*>(pointer));
1154 talk_base::scoped_refptr<VideoTrackInterface> track(
1155 reinterpret_cast<VideoTrackInterface*>(j_video_track_pointer));
1156 return stream->RemoveTrack(track);
1157}
1158
1159JOW(jstring, MediaStream_nativeLabel)(JNIEnv* jni, jclass, jlong j_p) {
1160 return JavaStringFromStdString(
1161 jni, reinterpret_cast<MediaStreamInterface*>(j_p)->label());
1162}
1163
1164JOW(void, MediaStream_free)(JNIEnv*, jclass, jlong j_p) {
1165 reinterpret_cast<MediaStreamInterface*>(j_p)->Release();
1166}
1167
1168JOW(jlong, PeerConnectionFactory_nativeCreateObserver)(
1169 JNIEnv * jni, jclass, jobject j_observer) {
1170 return (jlong)new PCOJava(jni, j_observer);
1171}
1172
1173#ifdef ANDROID
1174JOW(jboolean, PeerConnectionFactory_initializeAndroidGlobals)(
1175 JNIEnv* jni, jclass, jobject context) {
1176 CHECK(g_jvm, "JNI_OnLoad failed to run?");
1177 bool failure = false;
1178 failure |= webrtc::VideoEngine::SetAndroidObjects(g_jvm, context);
1179 failure |= webrtc::VoiceEngine::SetAndroidObjects(g_jvm, jni, context);
1180 return !failure;
1181}
1182#endif // ANDROID
1183
1184JOW(jlong, PeerConnectionFactory_nativeCreatePeerConnectionFactory)(
1185 JNIEnv* jni, jclass) {
1186 talk_base::scoped_refptr<PeerConnectionFactoryInterface> factory(
1187 webrtc::CreatePeerConnectionFactory());
1188 return (jlong)factory.release();
1189}
1190
1191JOW(void, PeerConnectionFactory_freeFactory)(JNIEnv*, jclass, jlong j_p) {
1192 reinterpret_cast<PeerConnectionFactoryInterface*>(j_p)->Release();
1193}
1194
1195JOW(jlong, PeerConnectionFactory_nativeCreateLocalMediaStream)(
1196 JNIEnv* jni, jclass, jlong native_factory, jstring label) {
1197 talk_base::scoped_refptr<PeerConnectionFactoryInterface> factory(
1198 reinterpret_cast<PeerConnectionFactoryInterface*>(native_factory));
1199 talk_base::scoped_refptr<MediaStreamInterface> stream(
1200 factory->CreateLocalMediaStream(JavaToStdString(jni, label)));
1201 return (jlong)stream.release();
1202}
1203
1204JOW(jlong, PeerConnectionFactory_nativeCreateVideoSource)(
1205 JNIEnv* jni, jclass, jlong native_factory, jlong native_capturer,
1206 jobject j_constraints) {
1207 talk_base::scoped_ptr<ConstraintsWrapper> constraints(
1208 new ConstraintsWrapper(jni, j_constraints));
1209 talk_base::scoped_refptr<PeerConnectionFactoryInterface> factory(
1210 reinterpret_cast<PeerConnectionFactoryInterface*>(native_factory));
1211 talk_base::scoped_refptr<VideoSourceInterface> source(
1212 factory->CreateVideoSource(
1213 reinterpret_cast<cricket::VideoCapturer*>(native_capturer),
1214 constraints.get()));
1215 return (jlong)source.release();
1216}
1217
1218JOW(jlong, PeerConnectionFactory_nativeCreateVideoTrack)(
1219 JNIEnv* jni, jclass, jlong native_factory, jstring id,
1220 jlong native_source) {
1221 talk_base::scoped_refptr<PeerConnectionFactoryInterface> factory(
1222 reinterpret_cast<PeerConnectionFactoryInterface*>(native_factory));
1223 talk_base::scoped_refptr<VideoTrackInterface> track(
1224 factory->CreateVideoTrack(
1225 JavaToStdString(jni, id),
1226 reinterpret_cast<VideoSourceInterface*>(native_source)));
1227 return (jlong)track.release();
1228}
1229
1230JOW(jlong, PeerConnectionFactory_nativeCreateAudioTrack)(
1231 JNIEnv* jni, jclass, jlong native_factory, jstring id) {
1232 talk_base::scoped_refptr<PeerConnectionFactoryInterface> factory(
1233 reinterpret_cast<PeerConnectionFactoryInterface*>(native_factory));
1234 talk_base::scoped_refptr<AudioTrackInterface> track(
1235 factory->CreateAudioTrack(JavaToStdString(jni, id), NULL));
1236 return (jlong)track.release();
1237}
1238
1239static void JavaIceServersToJsepIceServers(
1240 JNIEnv* jni, jobject j_ice_servers,
1241 PeerConnectionInterface::IceServers* ice_servers) {
1242 jclass list_class = GetObjectClass(jni, j_ice_servers);
1243 jmethodID iterator_id = GetMethodID(
1244 jni, list_class, "iterator", "()Ljava/util/Iterator;");
1245 jobject iterator = jni->CallObjectMethod(j_ice_servers, iterator_id);
1246 CHECK_EXCEPTION(jni, "error during CallObjectMethod");
1247 jmethodID iterator_has_next = GetMethodID(
1248 jni, GetObjectClass(jni, iterator), "hasNext", "()Z");
1249 jmethodID iterator_next = GetMethodID(
1250 jni, GetObjectClass(jni, iterator), "next", "()Ljava/lang/Object;");
1251 while (jni->CallBooleanMethod(iterator, iterator_has_next)) {
1252 CHECK_EXCEPTION(jni, "error during CallBooleanMethod");
1253 jobject j_ice_server = jni->CallObjectMethod(iterator, iterator_next);
1254 CHECK_EXCEPTION(jni, "error during CallObjectMethod");
1255 jclass j_ice_server_class = GetObjectClass(jni, j_ice_server);
1256 jfieldID j_ice_server_uri_id =
1257 GetFieldID(jni, j_ice_server_class, "uri", "Ljava/lang/String;");
1258 jfieldID j_ice_server_username_id =
1259 GetFieldID(jni, j_ice_server_class, "username", "Ljava/lang/String;");
1260 jfieldID j_ice_server_password_id =
1261 GetFieldID(jni, j_ice_server_class, "password", "Ljava/lang/String;");
1262 jstring uri = reinterpret_cast<jstring>(
1263 GetObjectField(jni, j_ice_server, j_ice_server_uri_id));
1264 jstring username = reinterpret_cast<jstring>(
1265 GetObjectField(jni, j_ice_server, j_ice_server_username_id));
1266 jstring password = reinterpret_cast<jstring>(
1267 GetObjectField(jni, j_ice_server, j_ice_server_password_id));
1268 PeerConnectionInterface::IceServer server;
1269 server.uri = JavaToStdString(jni, uri);
1270 server.username = JavaToStdString(jni, username);
1271 server.password = JavaToStdString(jni, password);
1272 ice_servers->push_back(server);
1273 }
1274 CHECK_EXCEPTION(jni, "error during CallBooleanMethod");
1275}
1276
1277JOW(jlong, PeerConnectionFactory_nativeCreatePeerConnection)(
1278 JNIEnv *jni, jclass, jlong factory, jobject j_ice_servers,
1279 jobject j_constraints, jlong observer_p) {
1280 talk_base::scoped_refptr<PeerConnectionFactoryInterface> f(
1281 reinterpret_cast<PeerConnectionFactoryInterface*>(factory));
1282 PeerConnectionInterface::IceServers servers;
1283 JavaIceServersToJsepIceServers(jni, j_ice_servers, &servers);
1284 PCOJava* observer = reinterpret_cast<PCOJava*>(observer_p);
1285 observer->SetConstraints(new ConstraintsWrapper(jni, j_constraints));
1286 talk_base::scoped_refptr<PeerConnectionInterface> pc(f->CreatePeerConnection(
1287 servers, observer->constraints(), NULL, observer));
1288 return (jlong)pc.release();
1289}
1290
1291static talk_base::scoped_refptr<PeerConnectionInterface> ExtractNativePC(
1292 JNIEnv* jni, jobject j_pc) {
1293 jfieldID native_pc_id = GetFieldID(jni,
1294 GetObjectClass(jni, j_pc), "nativePeerConnection", "J");
1295 jlong j_p = GetLongField(jni, j_pc, native_pc_id);
1296 return talk_base::scoped_refptr<PeerConnectionInterface>(
1297 reinterpret_cast<PeerConnectionInterface*>(j_p));
1298}
1299
1300JOW(jobject, PeerConnection_getLocalDescription)(JNIEnv* jni, jobject j_pc) {
1301 const SessionDescriptionInterface* sdp =
1302 ExtractNativePC(jni, j_pc)->local_description();
1303 return sdp ? JavaSdpFromNativeSdp(jni, sdp) : NULL;
1304}
1305
1306JOW(jobject, PeerConnection_getRemoteDescription)(JNIEnv* jni, jobject j_pc) {
1307 const SessionDescriptionInterface* sdp =
1308 ExtractNativePC(jni, j_pc)->remote_description();
1309 return sdp ? JavaSdpFromNativeSdp(jni, sdp) : NULL;
1310}
1311
henrike@webrtc.org723d6832013-07-12 16:04:50 +00001312JOW(jobject, PeerConnection_createDataChannel)(
1313 JNIEnv* jni, jobject j_pc, jstring j_label, jobject j_init) {
1314 DataChannelInit init = JavaDataChannelInitToNative(jni, j_init);
1315 talk_base::scoped_refptr<DataChannelInterface> channel(
1316 ExtractNativePC(jni, j_pc)->CreateDataChannel(
1317 JavaToStdString(jni, j_label), &init));
1318 jclass j_data_channel_class = FindClass(jni, "org/webrtc/DataChannel");
1319 jmethodID j_data_channel_ctor = GetMethodID(
1320 jni, j_data_channel_class, "<init>", "(J)V");
1321 jobject j_channel = jni->NewObject(
1322 j_data_channel_class, j_data_channel_ctor, channel.get());
1323 CHECK_EXCEPTION(jni, "error during NewObject");
1324 // Channel is now owned by Java object, and will be freed from there.
1325 channel->AddRef();
1326 return j_channel;
1327}
1328
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001329JOW(void, PeerConnection_createOffer)(
1330 JNIEnv* jni, jobject j_pc, jobject j_observer, jobject j_constraints) {
1331 ConstraintsWrapper* constraints =
1332 new ConstraintsWrapper(jni, j_constraints);
1333 talk_base::scoped_refptr<CreateSdpObserverWrapper> observer(
1334 new talk_base::RefCountedObject<CreateSdpObserverWrapper>(
1335 jni, j_observer, constraints));
1336 ExtractNativePC(jni, j_pc)->CreateOffer(observer, constraints);
1337}
1338
1339JOW(void, PeerConnection_createAnswer)(
1340 JNIEnv* jni, jobject j_pc, jobject j_observer, jobject j_constraints) {
1341 ConstraintsWrapper* constraints =
1342 new ConstraintsWrapper(jni, j_constraints);
1343 talk_base::scoped_refptr<CreateSdpObserverWrapper> observer(
1344 new talk_base::RefCountedObject<CreateSdpObserverWrapper>(
1345 jni, j_observer, constraints));
1346 ExtractNativePC(jni, j_pc)->CreateAnswer(observer, constraints);
1347}
1348
1349// Helper to create a SessionDescriptionInterface from a SessionDescription.
1350static SessionDescriptionInterface* JavaSdpToNativeSdp(
1351 JNIEnv* jni, jobject j_sdp) {
1352 jfieldID j_type_id = GetFieldID(
1353 jni, GetObjectClass(jni, j_sdp), "type",
1354 "Lorg/webrtc/SessionDescription$Type;");
1355 jobject j_type = GetObjectField(jni, j_sdp, j_type_id);
1356 jmethodID j_canonical_form_id = GetMethodID(
1357 jni, GetObjectClass(jni, j_type), "canonicalForm",
1358 "()Ljava/lang/String;");
1359 jstring j_type_string = (jstring)jni->CallObjectMethod(
1360 j_type, j_canonical_form_id);
1361 CHECK_EXCEPTION(jni, "error during CallObjectMethod");
1362 std::string std_type = JavaToStdString(jni, j_type_string);
1363
1364 jfieldID j_description_id = GetFieldID(
1365 jni, GetObjectClass(jni, j_sdp), "description", "Ljava/lang/String;");
1366 jstring j_description = (jstring)GetObjectField(jni, j_sdp, j_description_id);
1367 std::string std_description = JavaToStdString(jni, j_description);
1368
1369 return webrtc::CreateSessionDescription(
1370 std_type, std_description, NULL);
1371}
1372
1373JOW(void, PeerConnection_setLocalDescription)(
1374 JNIEnv* jni, jobject j_pc,
1375 jobject j_observer, jobject j_sdp) {
1376 talk_base::scoped_refptr<SetSdpObserverWrapper> observer(
1377 new talk_base::RefCountedObject<SetSdpObserverWrapper>(
1378 jni, j_observer, reinterpret_cast<ConstraintsWrapper*>(NULL)));
1379 ExtractNativePC(jni, j_pc)->SetLocalDescription(
1380 observer, JavaSdpToNativeSdp(jni, j_sdp));
1381}
1382
1383JOW(void, PeerConnection_setRemoteDescription)(
1384 JNIEnv* jni, jobject j_pc,
1385 jobject j_observer, jobject j_sdp) {
1386 talk_base::scoped_refptr<SetSdpObserverWrapper> observer(
1387 new talk_base::RefCountedObject<SetSdpObserverWrapper>(
1388 jni, j_observer, reinterpret_cast<ConstraintsWrapper*>(NULL)));
1389 ExtractNativePC(jni, j_pc)->SetRemoteDescription(
1390 observer, JavaSdpToNativeSdp(jni, j_sdp));
1391}
1392
1393JOW(jboolean, PeerConnection_updateIce)(
1394 JNIEnv* jni, jobject j_pc, jobject j_ice_servers, jobject j_constraints) {
1395 PeerConnectionInterface::IceServers ice_servers;
1396 JavaIceServersToJsepIceServers(jni, j_ice_servers, &ice_servers);
1397 talk_base::scoped_ptr<ConstraintsWrapper> constraints(
1398 new ConstraintsWrapper(jni, j_constraints));
1399 return ExtractNativePC(jni, j_pc)->UpdateIce(ice_servers, constraints.get());
1400}
1401
1402JOW(jboolean, PeerConnection_nativeAddIceCandidate)(
1403 JNIEnv* jni, jobject j_pc, jstring j_sdp_mid,
1404 jint j_sdp_mline_index, jstring j_candidate_sdp) {
1405 std::string sdp_mid = JavaToStdString(jni, j_sdp_mid);
1406 std::string sdp = JavaToStdString(jni, j_candidate_sdp);
1407 talk_base::scoped_ptr<IceCandidateInterface> candidate(
1408 webrtc::CreateIceCandidate(sdp_mid, j_sdp_mline_index, sdp, NULL));
1409 return ExtractNativePC(jni, j_pc)->AddIceCandidate(candidate.get());
1410}
1411
1412JOW(jboolean, PeerConnection_nativeAddLocalStream)(
1413 JNIEnv* jni, jobject j_pc, jlong native_stream, jobject j_constraints) {
1414 talk_base::scoped_ptr<ConstraintsWrapper> constraints(
1415 new ConstraintsWrapper(jni, j_constraints));
1416 return ExtractNativePC(jni, j_pc)->AddStream(
1417 reinterpret_cast<MediaStreamInterface*>(native_stream),
1418 constraints.get());
1419}
1420
1421JOW(void, PeerConnection_nativeRemoveLocalStream)(
1422 JNIEnv* jni, jobject j_pc, jlong native_stream) {
1423 ExtractNativePC(jni, j_pc)->RemoveStream(
1424 reinterpret_cast<MediaStreamInterface*>(native_stream));
1425}
1426
1427JOW(bool, PeerConnection_nativeGetStats)(
1428 JNIEnv* jni, jobject j_pc, jobject j_observer, jlong native_track) {
1429 talk_base::scoped_refptr<StatsObserverWrapper> observer(
1430 new talk_base::RefCountedObject<StatsObserverWrapper>(jni, j_observer));
1431 return ExtractNativePC(jni, j_pc)->GetStats(
1432 observer, reinterpret_cast<MediaStreamTrackInterface*>(native_track));
1433}
1434
1435JOW(jobject, PeerConnection_signalingState)(JNIEnv* jni, jobject j_pc) {
1436 PeerConnectionInterface::SignalingState state =
1437 ExtractNativePC(jni, j_pc)->signaling_state();
1438 return JavaEnumFromIndex(jni, "PeerConnection$SignalingState", state);
1439}
1440
1441JOW(jobject, PeerConnection_iceConnectionState)(JNIEnv* jni, jobject j_pc) {
1442 PeerConnectionInterface::IceConnectionState state =
1443 ExtractNativePC(jni, j_pc)->ice_connection_state();
1444 return JavaEnumFromIndex(jni, "PeerConnection$IceConnectionState", state);
1445}
1446
1447JOW(jobject, PeerGathering_iceGatheringState)(JNIEnv* jni, jobject j_pc) {
1448 PeerConnectionInterface::IceGatheringState state =
1449 ExtractNativePC(jni, j_pc)->ice_gathering_state();
1450 return JavaEnumFromIndex(jni, "PeerGathering$IceGatheringState", state);
1451}
1452
1453JOW(void, PeerConnection_close)(JNIEnv* jni, jobject j_pc) {
1454 ExtractNativePC(jni, j_pc)->Close();
1455 return;
1456}
1457
1458JOW(jobject, MediaSource_nativeState)(JNIEnv* jni, jclass, jlong j_p) {
1459 talk_base::scoped_refptr<MediaSourceInterface> p(
1460 reinterpret_cast<MediaSourceInterface*>(j_p));
1461 return JavaEnumFromIndex(jni, "MediaSource$State", p->state());
1462}
1463
1464JOW(jlong, VideoCapturer_nativeCreateVideoCapturer)(
1465 JNIEnv* jni, jclass, jstring j_device_name) {
1466 std::string device_name = JavaToStdString(jni, j_device_name);
1467 talk_base::scoped_ptr<cricket::DeviceManagerInterface> device_manager(
1468 cricket::DeviceManagerFactory::Create());
1469 CHECK(device_manager->Init(), "DeviceManager::Init() failed");
1470 cricket::Device device;
1471 if (!device_manager->GetVideoCaptureDevice(device_name, &device)) {
1472 LOG(LS_ERROR) << "GetVideoCaptureDevice failed";
1473 return 0;
1474 }
1475 talk_base::scoped_ptr<cricket::VideoCapturer> capturer(
1476 device_manager->CreateVideoCapturer(device));
1477 return (jlong)capturer.release();
1478}
1479
1480JOW(jlong, VideoRenderer_nativeCreateGuiVideoRenderer)(
1481 JNIEnv* jni, jclass, int x, int y) {
1482 talk_base::scoped_ptr<VideoRendererWrapper> renderer(
1483 VideoRendererWrapper::Create(
1484 cricket::VideoRendererFactory::CreateGuiVideoRenderer(x, y)));
1485 return (jlong)renderer.release();
1486}
1487
1488JOW(jlong, VideoRenderer_nativeWrapVideoRenderer)(
1489 JNIEnv* jni, jclass, jobject j_callbacks) {
1490 talk_base::scoped_ptr<JavaVideoRendererWrapper> renderer(
1491 new JavaVideoRendererWrapper(jni, j_callbacks));
1492 return (jlong)renderer.release();
1493}
1494
1495JOW(jstring, MediaStreamTrack_nativeId)(JNIEnv* jni, jclass, jlong j_p) {
1496 talk_base::scoped_refptr<MediaStreamTrackInterface> p(
1497 reinterpret_cast<MediaStreamTrackInterface*>(j_p));
1498 return JavaStringFromStdString(jni, p->id());
1499}
1500
1501JOW(jstring, MediaStreamTrack_nativeKind)(JNIEnv* jni, jclass, jlong j_p) {
1502 talk_base::scoped_refptr<MediaStreamTrackInterface> p(
1503 reinterpret_cast<MediaStreamTrackInterface*>(j_p));
1504 return JavaStringFromStdString(jni, p->kind());
1505}
1506
1507JOW(jboolean, MediaStreamTrack_nativeEnabled)(JNIEnv* jni, jclass, jlong j_p) {
1508 talk_base::scoped_refptr<MediaStreamTrackInterface> p(
1509 reinterpret_cast<MediaStreamTrackInterface*>(j_p));
1510 return p->enabled();
1511}
1512
1513JOW(jobject, MediaStreamTrack_nativeState)(JNIEnv* jni, jclass, jlong j_p) {
1514 talk_base::scoped_refptr<MediaStreamTrackInterface> p(
1515 reinterpret_cast<MediaStreamTrackInterface*>(j_p));
1516 return JavaEnumFromIndex(jni, "MediaStreamTrack$State", p->state());
1517}
1518
1519JOW(jboolean, MediaStreamTrack_nativeSetState)(
1520 JNIEnv* jni, jclass, jlong j_p, jint j_new_state) {
1521 talk_base::scoped_refptr<MediaStreamTrackInterface> p(
1522 reinterpret_cast<MediaStreamTrackInterface*>(j_p));
1523 MediaStreamTrackInterface::TrackState new_state =
1524 (MediaStreamTrackInterface::TrackState)j_new_state;
1525 return p->set_state(new_state);
1526}
1527
1528JOW(jboolean, MediaStreamTrack_nativeSetEnabled)(
1529 JNIEnv* jni, jclass, jlong j_p, jboolean enabled) {
1530 talk_base::scoped_refptr<MediaStreamTrackInterface> p(
1531 reinterpret_cast<MediaStreamTrackInterface*>(j_p));
1532 return p->set_enabled(enabled);
1533}
1534
1535JOW(void, VideoTrack_nativeAddRenderer)(
1536 JNIEnv* jni, jclass,
1537 jlong j_video_track_pointer, jlong j_renderer_pointer) {
1538 talk_base::scoped_refptr<VideoTrackInterface> track(
1539 reinterpret_cast<VideoTrackInterface*>(j_video_track_pointer));
1540 track->AddRenderer(
1541 reinterpret_cast<VideoRendererInterface*>(j_renderer_pointer));
1542}
1543
1544JOW(void, VideoTrack_nativeRemoveRenderer)(
1545 JNIEnv* jni, jclass,
1546 jlong j_video_track_pointer, jlong j_renderer_pointer) {
1547 talk_base::scoped_refptr<VideoTrackInterface> track(
1548 reinterpret_cast<VideoTrackInterface*>(j_video_track_pointer));
1549 track->RemoveRenderer(
1550 reinterpret_cast<VideoRendererInterface*>(j_renderer_pointer));
1551}