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