blob: ec531ef87b571562ac09531916465b2ddcadb217
1 | #define LOG_TAG "Tv-JNI" |
2 | #include <utils/Log.h> |
3 | |
4 | #include "jni.h" |
5 | #include "JNIHelp.h" |
6 | #include "android_runtime/AndroidRuntime.h" |
7 | #include <utils/Vector.h> |
8 | #include <include/Tv.h> |
9 | #include <binder/IMemory.h> |
10 | #include <binder/Parcel.h> |
11 | #include <binder/MemoryHeapBase.h> |
12 | #include <binder/MemoryBase.h> |
13 | #include <core/SkBitmap.h> |
14 | #include "android_util_Binder.h" |
15 | #include "android_os_Parcel.h" |
16 | using namespace android; |
17 | |
18 | struct fields_t { |
19 | jfieldID context; |
20 | jmethodID post_event; |
21 | }; |
22 | |
23 | #ifdef LOG_TAG |
24 | #undef LOG_TAG |
25 | #define LOG_TAG "TvJNI" |
26 | #endif |
27 | |
28 | static fields_t fields; |
29 | static Mutex sLock; |
30 | class JNITvContext: public TvListener |
31 | { |
32 | public: |
33 | JNITvContext(JNIEnv *env, jobject weak_this, jclass clazz, const sp<Tv> &tv); |
34 | ~JNITvContext() |
35 | { |
36 | release(); |
37 | } |
38 | virtual void notify(int32_t msgType, const Parcel &p); |
39 | void addCallbackBuffer(JNIEnv *env, jbyteArray cbb); |
40 | sp<Tv> getTv() |
41 | { |
42 | Mutex::Autolock _l(mLock); |
43 | return mTv; |
44 | } |
45 | void release(); |
46 | Parcel *mExtParcel; |
47 | private: |
48 | jobject mTvJObjectWeak; // weak reference to java object |
49 | jclass mTvJClass; // strong reference to java class |
50 | sp<Tv> mTv; // strong reference to native object |
51 | Mutex mLock; |
52 | |
53 | Vector<jbyteArray> mCallbackBuffers; // Global reference application managed byte[] |
54 | bool mManualBufferMode; // Whether to use application managed buffers. |
55 | bool mManualTvCallbackSet; // Whether the callback has been set, used to reduce unnecessary calls to set the callback. |
56 | }; |
57 | |
58 | sp<Tv> get_native_tv(JNIEnv *env, jobject thiz, JNITvContext **pContext) |
59 | { |
60 | sp<Tv> tv; |
61 | Mutex::Autolock _l(sLock); |
62 | JNITvContext *context = reinterpret_cast<JNITvContext *>(env->GetIntField(thiz, fields.context)); |
63 | if (context != NULL) { |
64 | tv = context->getTv(); |
65 | } |
66 | if (tv == 0) { |
67 | jniThrowException(env, "java/lang/RuntimeException", "Method called after release()"); |
68 | } |
69 | |
70 | if (pContext != NULL) *pContext = context; |
71 | return tv; |
72 | } |
73 | |
74 | JNITvContext::JNITvContext(JNIEnv *env, jobject weak_this, jclass clazz, const sp<Tv> &tv) |
75 | { |
76 | mTvJObjectWeak = env->NewGlobalRef(weak_this); |
77 | mTvJClass = (jclass)env->NewGlobalRef(clazz); |
78 | mTv = tv; |
79 | ALOGD("tvjni----------------------JNITvContext::JNITvContext("); |
80 | mManualBufferMode = false; |
81 | mManualTvCallbackSet = false; |
82 | //mExtParcel = parcelForJavaObject(env, ext_parcel); |
83 | } |
84 | |
85 | void JNITvContext::release() |
86 | { |
87 | ALOGD("release"); |
88 | Mutex::Autolock _l(mLock); |
89 | JNIEnv *env = AndroidRuntime::getJNIEnv(); |
90 | |
91 | if (mTvJObjectWeak != NULL) { |
92 | env->DeleteGlobalRef(mTvJObjectWeak); |
93 | mTvJObjectWeak = NULL; |
94 | } |
95 | if (mTvJClass != NULL) { |
96 | env->DeleteGlobalRef(mTvJClass); |
97 | mTvJClass = NULL; |
98 | } |
99 | mTv.clear(); |
100 | } |
101 | |
102 | // connect to tv service |
103 | static void android_amlogic_Tv_native_setup(JNIEnv *env, jobject thiz, jobject weak_this) |
104 | { |
105 | sp<Tv> tv = Tv::connect(); |
106 | |
107 | ALOGD("android_amlogic_Tv_native_setup."); |
108 | |
109 | if (tv == NULL) { |
110 | jniThrowException(env, "java/lang/RuntimeException", "Fail to connect to tv service"); |
111 | return; |
112 | } |
113 | |
114 | // make sure tv amlogic is alive |
115 | if (tv->getStatus() != NO_ERROR) { |
116 | jniThrowException(env, "java/lang/RuntimeException", "Tv initialization failed!"); |
117 | return; |
118 | } |
119 | |
120 | jclass clazz = env->GetObjectClass(thiz); |
121 | if (clazz == NULL) { |
122 | jniThrowException(env, "java/lang/RuntimeException", "Can't find android/amlogic/Tv!"); |
123 | return; |
124 | } |
125 | |
126 | sp<JNITvContext> context = new JNITvContext(env, weak_this, clazz, tv); |
127 | context->incStrong(thiz); |
128 | tv->setListener(context); |
129 | |
130 | env->SetIntField(thiz, fields.context, (int)context.get()); |
131 | } |
132 | |
133 | |
134 | static void android_amlogic_Tv_release(JNIEnv *env, jobject thiz) |
135 | { |
136 | // TODO: Change to LOGE |
137 | JNITvContext *context = NULL; |
138 | sp<Tv> tv; |
139 | { |
140 | Mutex::Autolock _l(sLock); |
141 | context = reinterpret_cast<JNITvContext *>(env->GetIntField(thiz, fields.context)); |
142 | |
143 | // Make sure we do not attempt to callback on a deleted Java object. |
144 | env->SetIntField(thiz, fields.context, 0); |
145 | } |
146 | |
147 | ALOGD("release tv"); |
148 | |
149 | // clean up if release has not been called before |
150 | if (context != NULL) { |
151 | tv = context->getTv(); |
152 | context->release(); |
153 | ALOGD("native_release: context=%p tv=%p", context, tv.get()); |
154 | |
155 | // clear callbacks |
156 | if (tv != NULL) { |
157 | //tv->setPreviewCallbackFlags(FRAME_CALLBACK_FLAG_NOOP); |
158 | tv->disconnect(); |
159 | } |
160 | |
161 | // remove context to prevent further Java access |
162 | context->decStrong(thiz); |
163 | } |
164 | } |
165 | |
166 | void JNITvContext::notify(int32_t msgType, const Parcel &p) |
167 | { |
168 | // VM pointer will be NULL if object is released |
169 | Mutex::Autolock _l(mLock); |
170 | if (mTvJObjectWeak == NULL) { |
171 | ALOGW("callback on dead tv object"); |
172 | return; |
173 | } |
174 | |
175 | JNIEnv *env = AndroidRuntime::getJNIEnv(); |
176 | |
177 | jobject jParcel = createJavaParcelObject(env); |
178 | if (jParcel != NULL) { |
179 | Parcel *nativeParcel = parcelForJavaObject(env, jParcel); |
180 | nativeParcel->write(p.data(), p.dataSize()); |
181 | env->CallStaticVoidMethod(mTvJClass, fields.post_event, mTvJObjectWeak, msgType, jParcel); |
182 | env->DeleteLocalRef(jParcel); |
183 | } |
184 | } |
185 | |
186 | |
187 | void JNITvContext::addCallbackBuffer(JNIEnv *env, jbyteArray cbb) |
188 | { |
189 | if (cbb != NULL) { |
190 | Mutex::Autolock _l(mLock); |
191 | jbyteArray callbackBuffer = (jbyteArray)env->NewGlobalRef(cbb); |
192 | mCallbackBuffers.push(cbb); |
193 | ALOGD("Adding callback buffer to queue, %d total", mCallbackBuffers.size()); |
194 | } else { |
195 | ALOGE("Null byte array!"); |
196 | } |
197 | } |
198 | |
199 | static jint android_amlogic_Tv_processCmd(JNIEnv *env, jobject thiz, jobject pObj, jobject rObj) |
200 | { |
201 | sp<Tv> tv = get_native_tv(env, thiz, NULL); |
202 | if (tv == 0) return -1; |
203 | |
204 | Parcel *p = parcelForJavaObject(env, pObj); |
205 | //jclass clazz; |
206 | //clazz = env->FindClass("android/os/Parcel"); |
207 | //LOG_FATAL_IF(clazz == NULL, "Unable to find class android.os.Parcel"); |
208 | |
209 | |
210 | //jmethodID mConstructor = env->GetMethodID(clazz, "<init>", "(I)V"); |
211 | //jobject replayobj = env->NewObject(clazz, mConstructor, 0); |
212 | Parcel *r = parcelForJavaObject(env, rObj); |
213 | |
214 | |
215 | return tv->processCmd(*p, r); |
216 | //if ( != NO_ERROR) { |
217 | // jniThrowException(env, "java/lang/RuntimeException", "StartTv failed"); |
218 | // return -1; |
219 | // } |
220 | //return 0; |
221 | } |
222 | |
223 | static void android_amlogic_Tv_addCallbackBuffer(JNIEnv *env, jobject thiz, jbyteArray bytes) |
224 | { |
225 | JNITvContext *context = reinterpret_cast<JNITvContext *>(env->GetIntField(thiz, fields.context)); |
226 | |
227 | ALOGD("addCallbackBuffer"); |
228 | if (context != NULL) { |
229 | context->addCallbackBuffer(env, bytes); |
230 | } |
231 | } |
232 | |
233 | static void android_amlogic_Tv_reconnect(JNIEnv *env, jobject thiz) |
234 | { |
235 | sp<Tv> tv = get_native_tv(env, thiz, NULL); |
236 | if (tv == 0) return; |
237 | |
238 | if (tv->reconnect() != NO_ERROR) { |
239 | jniThrowException(env, "java/io/IOException", "reconnect failed"); |
240 | return; |
241 | } |
242 | } |
243 | |
244 | static void android_amlogic_Tv_lock(JNIEnv *env, jobject thiz) |
245 | { |
246 | sp<Tv> tv = get_native_tv(env, thiz, NULL); |
247 | if (tv == 0) return; |
248 | |
249 | ALOGD("lock"); |
250 | |
251 | if (tv->lock() != NO_ERROR) { |
252 | jniThrowException(env, "java/lang/RuntimeException", "lock failed"); |
253 | } |
254 | } |
255 | |
256 | static void android_amlogic_Tv_unlock(JNIEnv *env, jobject thiz) |
257 | { |
258 | sp<Tv> tv = get_native_tv(env, thiz, NULL); |
259 | if (tv == 0) return; |
260 | |
261 | ALOGD("unlock"); |
262 | |
263 | if (tv->unlock() != NO_ERROR) { |
264 | jniThrowException(env, "java/lang/RuntimeException", "unlock failed"); |
265 | } |
266 | } |
267 | |
268 | static void android_amlogic_Tv_create_subtitle_bitmap(JNIEnv *env, jobject thiz, jobject bmpobj) |
269 | { |
270 | ALOGD("create subtitle bmp"); |
271 | sp<Tv> tv = get_native_tv(env, thiz, NULL); |
272 | if (tv == 0) return; |
273 | |
274 | //get skbitmap |
275 | jclass bmp_clazz; |
276 | jfieldID skbmp_fid; |
277 | jint hbmp; |
278 | bmp_clazz = env->FindClass("android/graphics/Bitmap"); |
279 | skbmp_fid = env->GetFieldID(bmp_clazz, "mNativeBitmap", "I"); |
280 | hbmp = env->GetIntField(bmpobj, skbmp_fid); |
281 | SkBitmap *pSkBmp = reinterpret_cast<SkBitmap *>(hbmp); |
282 | ALOGD("pSkBmp = %d", hbmp); |
283 | ALOGD("bmp width = %d height = %d", pSkBmp->width(), pSkBmp->height()); |
284 | env->DeleteLocalRef(bmp_clazz); |
285 | |
286 | //alloc share mem |
287 | sp<MemoryHeapBase> MemHeap = new MemoryHeapBase(1920 * 1080 * 4, 0, "subtitle bmp"); |
288 | ALOGD("heap id = %d", MemHeap->getHeapID()); |
289 | if (MemHeap->getHeapID() < 0) { |
290 | return; |
291 | } |
292 | sp<MemoryBase> MemBase = new MemoryBase(MemHeap, 0, 1920 * 1080 * 4); |
293 | pSkBmp->setPixels(MemBase->pointer()); |
294 | |
295 | |
296 | //send share mem to server |
297 | tv->createSubtitle(MemBase); |
298 | return; |
299 | } |
300 | |
301 | static void android_amlogic_Tv_create_video_frame_bitmap(JNIEnv *env, jobject thiz, jobject bmpobj ) |
302 | { |
303 | ALOGD("create video frame bmp"); |
304 | sp<Tv> tv = get_native_tv(env, thiz, NULL); |
305 | if (tv == 0) return; |
306 | |
307 | //get skbitmap |
308 | jclass bmp_clazz; |
309 | jfieldID skbmp_fid; |
310 | jint hbmp; |
311 | bmp_clazz = env->FindClass("android/graphics/Bitmap"); |
312 | skbmp_fid = env->GetFieldID(bmp_clazz, "mNativeBitmap", "I"); |
313 | hbmp = env->GetIntField(bmpobj, skbmp_fid); |
314 | SkBitmap *pSkBmp = reinterpret_cast<SkBitmap *>(hbmp); |
315 | ALOGD("pSkBmp = %d", hbmp); |
316 | ALOGD("bmp width = %d height = %d", pSkBmp->width(), pSkBmp->height()); |
317 | env->DeleteLocalRef(bmp_clazz); |
318 | |
319 | //alloc share mem |
320 | sp<MemoryHeapBase> MemHeap = new MemoryHeapBase(1280 * 720 * 4 + 1, 0, "video frame bmp"); |
321 | ALOGD("heap id = %d", MemHeap->getHeapID()); |
322 | if (MemHeap->getHeapID() < 0) { |
323 | return; |
324 | } |
325 | sp<MemoryBase> MemBase = new MemoryBase(MemHeap, 0, 1280 * 720 * 4 + 1); |
326 | pSkBmp->setPixels(MemBase->pointer()); |
327 | |
328 | |
329 | //send share mem to server |
330 | tv->createVideoFrame(MemBase); |
331 | return; |
332 | } |
333 | |
334 | //------------------------------------------------- |
335 | |
336 | static JNINativeMethod camMethods[] = { |
337 | { |
338 | "native_setup", |
339 | "(Ljava/lang/Object;)V", |
340 | (void *)android_amlogic_Tv_native_setup |
341 | }, |
342 | { |
343 | "native_release", |
344 | "()V", |
345 | (void *)android_amlogic_Tv_release |
346 | }, |
347 | { |
348 | "processCmd", |
349 | "(Landroid/os/Parcel;Landroid/os/Parcel;)I", |
350 | (void *)android_amlogic_Tv_processCmd |
351 | }, |
352 | { |
353 | "addCallbackBuffer", |
354 | "([B)V", |
355 | (void *)android_amlogic_Tv_addCallbackBuffer |
356 | }, |
357 | { |
358 | "reconnect", |
359 | "()V", |
360 | (void *)android_amlogic_Tv_reconnect |
361 | }, |
362 | { |
363 | "lock", |
364 | "()V", |
365 | (void *)android_amlogic_Tv_lock |
366 | }, |
367 | { |
368 | "unlock", |
369 | "()V", |
370 | (void *)android_amlogic_Tv_unlock |
371 | }, |
372 | { |
373 | "native_create_subtitle_bitmap", |
374 | "(Ljava/lang/Object;)V", |
375 | (void *)android_amlogic_Tv_create_subtitle_bitmap |
376 | }, |
377 | { |
378 | "native_create_video_frame_bitmap", |
379 | "(Ljava/lang/Object;)V", |
380 | (void *)android_amlogic_Tv_create_video_frame_bitmap |
381 | }, |
382 | |
383 | }; |
384 | |
385 | struct field { |
386 | const char *class_name; |
387 | const char *field_name; |
388 | const char *field_type; |
389 | jfieldID *jfield; |
390 | }; |
391 | |
392 | static int find_fields(JNIEnv *env, field *fields, int count) |
393 | { |
394 | for (int i = 0; i < count; i++) { |
395 | field *f = &fields[i]; |
396 | jclass clazz = env->FindClass(f->class_name); |
397 | if (clazz == NULL) { |
398 | ALOGE("Can't find %s", f->class_name); |
399 | return -1; |
400 | } |
401 | |
402 | jfieldID field = env->GetFieldID(clazz, f->field_name, f->field_type); |
403 | if (field == NULL) { |
404 | ALOGE("Can't find %s.%s", f->class_name, f->field_name); |
405 | return -1; |
406 | } |
407 | |
408 | *(f->jfield) = field; |
409 | } |
410 | |
411 | return 0; |
412 | } |
413 | |
414 | // Get all the required offsets in java class and register native functions |
415 | int register_android_amlogic_Tv(JNIEnv *env) |
416 | { |
417 | field fields_to_find[] = { |
418 | { "android/amlogic/Tv", "mNativeContext", "I", &fields.context } |
419 | }; |
420 | |
421 | ALOGD("register_android_amlogic_Tv."); |
422 | |
423 | if (find_fields(env, fields_to_find, NELEM(fields_to_find)) < 0) |
424 | return -1; |
425 | |
426 | jclass clazz = env->FindClass("android/amlogic/Tv"); |
427 | fields.post_event = env->GetStaticMethodID(clazz, "postEventFromNative", "(Ljava/lang/Object;ILandroid/os/Parcel;)V"); |
428 | if (fields.post_event == NULL) { |
429 | ALOGE("Can't find android/amlogic/Tv.postEventFromNative"); |
430 | return -1; |
431 | } |
432 | |
433 | // Register native functions |
434 | return AndroidRuntime::registerNativeMethods(env, "android/amlogic/Tv", camMethods, NELEM(camMethods)); |
435 | } |
436 | |
437 | |
438 | jint JNI_OnLoad(JavaVM *vm, void *reserved) |
439 | { |
440 | JNIEnv *env = NULL; |
441 | jint result = -1; |
442 | |
443 | if (vm->GetEnv((void **) &env, JNI_VERSION_1_4) != JNI_OK) { |
444 | ALOGE("ERROR: GetEnv failed\n"); |
445 | goto bail; |
446 | } |
447 | assert(env != NULL); |
448 | |
449 | register_android_amlogic_Tv(env); |
450 | |
451 | /* success -- return valid version number */ |
452 | result = JNI_VERSION_1_4; |
453 | bail: |
454 | return result; |
455 | } |
456 | |
457 |