]> git.sesse.net Git - vlc/blobdiff - modules/codec/omxil/android_mediacodec.c
nativewindowpriv: add is_hw arg in setup
[vlc] / modules / codec / omxil / android_mediacodec.c
index 94359c3dae00f02e14b52b3d16989f9a9b6466e7..1eeee694f0fb8f6a5d308f8bc4802239a1faee85 100644 (file)
@@ -37,6 +37,7 @@
 #include <vlc_cpu.h>
 
 #include "../h264_nal.h"
+#include "../hevc_nal.h"
 #include <OMX_Core.h>
 #include <OMX_Component.h>
 #include "omxil_utils.h"
 #define INFO_OUTPUT_FORMAT_CHANGED  -2
 #define INFO_TRY_AGAIN_LATER        -1
 
-extern JavaVM *myVm;
+#define THREAD_NAME "android_mediacodec"
+
+extern int jni_attach_thread(JNIEnv **env, const char *thread_name);
+extern void jni_detach_thread();
 /* JNI functions to get/set an Android Surface object. */
 extern jobject jni_LockAndGetAndroidJavaSurface();
 extern void jni_UnlockAndroidSurface();
@@ -301,13 +305,16 @@ static int OpenDecoder(vlc_object_t *p_this)
 
     const char *mime = NULL;
     switch (p_dec->fmt_in.i_codec) {
+    case VLC_CODEC_HEVC: mime = "video/hevc"; break;
     case VLC_CODEC_H264: mime = "video/avc"; break;
     case VLC_CODEC_H263: mime = "video/3gpp"; break;
     case VLC_CODEC_MP4V: mime = "video/mp4v-es"; break;
+    case VLC_CODEC_WMV3: mime = "video/x-ms-wmv"; break;
     case VLC_CODEC_VC1:  mime = "video/wvc1"; break;
     case VLC_CODEC_VP8:  mime = "video/x-vnd.on2.vp8"; break;
+    case VLC_CODEC_VP9:  mime = "video/x-vnd.on2.vp9"; break;
     default:
-        msg_Dbg(p_dec, "codec %d not supported", p_dec->fmt_in.i_codec);
+        msg_Dbg(p_dec, "codec %4.4s not supported", (char *)&p_dec->fmt_in.i_codec);
         return VLC_EGENERIC;
     }
 
@@ -327,7 +334,7 @@ static int OpenDecoder(vlc_object_t *p_this)
     p_dec->b_need_packetized = true;
 
     JNIEnv* env = NULL;
-    (*myVm)->AttachCurrentThread(myVm, &env, NULL);
+    jni_attach_thread(&env, THREAD_NAME);
 
     for (int i = 0; classes[i].name; i++) {
         *(jclass*)((uint8_t*)p_sys + classes[i].offset) =
@@ -392,7 +399,8 @@ static int OpenDecoder(vlc_object_t *p_this)
         if ((*env)->ExceptionOccurred(env)) {
             msg_Warn(p_dec, "Exception occurred in MediaCodecInfo.getCapabilitiesForType");
             (*env)->ExceptionClear(env);
-            break;
+            (*env)->DeleteLocalRef(env, info);
+            continue;
         } else if (codec_capabilities) {
             profile_levels = (*env)->GetObjectField(env, codec_capabilities, p_sys->profile_levels_field);
             if (profile_levels)
@@ -437,6 +445,7 @@ static int OpenDecoder(vlc_object_t *p_this)
             p_sys->name[name_len] = '\0';
             (*env)->ReleaseStringUTFChars(env, name, name_ptr);
             codec_name = name;
+            (*env)->DeleteLocalRef(env, info);
             break;
         }
         (*env)->DeleteLocalRef(env, info);
@@ -477,6 +486,10 @@ static int OpenDecoder(vlc_object_t *p_this)
             convert_sps_pps(p_dec, p_dec->fmt_in.p_extra, p_dec->fmt_in.i_extra,
                             ptr, buf_size,
                             &size, &p_sys->nal_size);
+        } else if (p_dec->fmt_in.i_codec == VLC_CODEC_HEVC) {
+            convert_hevc_nal_units(p_dec, p_dec->fmt_in.p_extra,
+                                   p_dec->fmt_in.i_extra, ptr, buf_size,
+                                   &size, &p_sys->nal_size);
         } else {
             memcpy(ptr, p_dec->fmt_in.p_extra, size);
         }
@@ -537,7 +550,7 @@ static int OpenDecoder(vlc_object_t *p_this)
         goto error;
     (*env)->DeleteLocalRef(env, format);
 
-    (*myVm)->DetachCurrentThread(myVm);
+    jni_detach_thread();
 
     const int timestamp_fifo_size = 32;
     p_sys->timestamp_fifo = timestamp_FifoNew(timestamp_fifo_size);
@@ -547,7 +560,7 @@ static int OpenDecoder(vlc_object_t *p_this)
     return VLC_SUCCESS;
 
  error:
-    (*myVm)->DetachCurrentThread(myVm);
+    jni_detach_thread();
     CloseDecoder(p_this);
     return VLC_EGENERIC;
 }
@@ -565,21 +578,33 @@ static void CloseDecoder(vlc_object_t *p_this)
      * to prevent the vout from using destroyed output buffers. */
     if (p_sys->direct_rendering)
         InvalidateAllPictures(p_dec);
-    (*myVm)->AttachCurrentThread(myVm, &env, NULL);
+    jni_attach_thread(&env, THREAD_NAME);
     if (p_sys->input_buffers)
         (*env)->DeleteGlobalRef(env, p_sys->input_buffers);
     if (p_sys->output_buffers)
         (*env)->DeleteGlobalRef(env, p_sys->output_buffers);
     if (p_sys->codec) {
         if (p_sys->started)
+        {
             (*env)->CallVoidMethod(env, p_sys->codec, p_sys->stop);
+            if ((*env)->ExceptionOccurred(env)) {
+                msg_Err(p_dec, "Exception in MediaCodec.stop");
+                (*env)->ExceptionClear(env);
+            }
+        }
         if (p_sys->allocated)
+        {
             (*env)->CallVoidMethod(env, p_sys->codec, p_sys->release);
+            if ((*env)->ExceptionOccurred(env)) {
+                msg_Err(p_dec, "Exception in MediaCodec.release");
+                (*env)->ExceptionClear(env);
+            }
+        }
         (*env)->DeleteGlobalRef(env, p_sys->codec);
     }
     if (p_sys->buffer_info)
         (*env)->DeleteGlobalRef(env, p_sys->buffer_info);
-    (*myVm)->DetachCurrentThread(myVm);
+    jni_detach_thread();
 
     free(p_sys->name);
     ArchitectureSpecificCopyHooksDestroy(p_sys->pixel_format, &p_sys->architecture_specific_data);
@@ -613,14 +638,14 @@ static void DisplayBuffer(picture_sys_t* p_picsys, bool b_render)
 
     /* Release the MediaCodec buffer. */
     JNIEnv *env = NULL;
-    (*myVm)->AttachCurrentThread(myVm, &env, NULL);
+    jni_attach_thread(&env, THREAD_NAME);
     (*env)->CallVoidMethod(env, p_sys->codec, p_sys->release_output_buffer, i_index, b_render);
     if ((*env)->ExceptionOccurred(env)) {
         msg_Err(p_dec, "Exception in MediaCodec.releaseOutputBuffer (DisplayBuffer)");
         (*env)->ExceptionClear(env);
     }
 
-    (*myVm)->DetachCurrentThread(myVm);
+    jni_detach_thread();
     p_picsys->b_valid = false;
 
     vlc_mutex_unlock(get_android_opaque_mutex());
@@ -658,6 +683,7 @@ static void GetOutput(decoder_t *p_dec, JNIEnv *env, picture_t **pp_pic, jlong t
         int index = (*env)->CallIntMethod(env, p_sys->codec, p_sys->dequeue_output_buffer,
                                           p_sys->buffer_info, timeout);
         if ((*env)->ExceptionOccurred(env)) {
+            msg_Err(p_dec, "Exception in MediaCodec.dequeueOutputBuffer (GetOutput)");
             (*env)->ExceptionClear(env);
             p_sys->error_state = true;
             return;
@@ -667,6 +693,12 @@ static void GetOutput(decoder_t *p_dec, JNIEnv *env, picture_t **pp_pic, jlong t
             if (!p_sys->pixel_format) {
                 msg_Warn(p_dec, "Buffers returned before output format is set, dropping frame");
                 (*env)->CallVoidMethod(env, p_sys->codec, p_sys->release_output_buffer, index, false);
+                if ((*env)->ExceptionOccurred(env)) {
+                    msg_Err(p_dec, "Exception in MediaCodec.releaseOutputBuffer");
+                    (*env)->ExceptionClear(env);
+                    p_sys->error_state = true;
+                    return;
+                }
                 continue;
             }
 
@@ -677,6 +709,13 @@ static void GetOutput(decoder_t *p_dec, JNIEnv *env, picture_t **pp_pic, jlong t
                 picture_sys_t *p_picsys = p_pic->p_sys;
                 int i_prev_index = p_picsys->i_index;
                 (*env)->CallVoidMethod(env, p_sys->codec, p_sys->release_output_buffer, i_prev_index, false);
+                if ((*env)->ExceptionOccurred(env)) {
+                    msg_Err(p_dec, "Exception in MediaCodec.releaseOutputBuffer " \
+                            "(GetOutput, overwriting previous picture)");
+                    (*env)->ExceptionClear(env);
+                    p_sys->error_state = true;
+                    return;
+                }
 
                 // No need to lock here since the previous picture was not sent.
                 p_sys->inflight_picture[i_prev_index] = NULL;
@@ -708,10 +747,10 @@ static void GetOutput(decoder_t *p_dec, JNIEnv *env, picture_t **pp_pic, jlong t
                     p_sys->inflight_picture[index] = p_pic;
                 } else {
                     jobject buf = (*env)->GetObjectArrayElement(env, p_sys->output_buffers, index);
-                    jsize buf_size = (*env)->GetDirectBufferCapacity(env, buf);
+                    //jsize buf_size = (*env)->GetDirectBufferCapacity(env, buf);
                     uint8_t *ptr = (*env)->GetDirectBufferAddress(env, buf);
 
-                    int size = (*env)->GetIntField(env, p_sys->buffer_info, p_sys->size_field);
+                    //int size = (*env)->GetIntField(env, p_sys->buffer_info, p_sys->size_field);
                     int offset = (*env)->GetIntField(env, p_sys->buffer_info, p_sys->offset_field);
                     ptr += offset; // Check the size parameter as well
 
@@ -737,6 +776,11 @@ static void GetOutput(decoder_t *p_dec, JNIEnv *env, picture_t **pp_pic, jlong t
             } else {
                 msg_Warn(p_dec, "NewPicture failed");
                 (*env)->CallVoidMethod(env, p_sys->codec, p_sys->release_output_buffer, index, false);
+                if ((*env)->ExceptionOccurred(env)) {
+                    msg_Err(p_dec, "Exception in MediaCodec.releaseOutputBuffer (GetOutput)");
+                    (*env)->ExceptionClear(env);
+                    p_sys->error_state = true;
+                }
             }
             return;
 
@@ -746,6 +790,14 @@ static void GetOutput(decoder_t *p_dec, JNIEnv *env, picture_t **pp_pic, jlong t
 
             p_sys->output_buffers = (*env)->CallObjectMethod(env, p_sys->codec,
                                                              p_sys->get_output_buffers);
+            if ((*env)->ExceptionOccurred(env)) {
+                msg_Err(p_dec, "Exception in MediaCodec.getOutputBuffer (GetOutput)");
+                (*env)->ExceptionClear(env);
+                p_sys->output_buffers = NULL;
+                p_sys->error_state = true;
+                return;
+            }
+
             p_sys->output_buffers = (*env)->NewGlobalRef(env, p_sys->output_buffers);
 
             vlc_mutex_lock(get_android_opaque_mutex());
@@ -756,6 +808,13 @@ static void GetOutput(decoder_t *p_dec, JNIEnv *env, picture_t **pp_pic, jlong t
 
         } else if (index == INFO_OUTPUT_FORMAT_CHANGED) {
             jobject format = (*env)->CallObjectMethod(env, p_sys->codec, p_sys->get_output_format);
+            if ((*env)->ExceptionOccurred(env)) {
+                msg_Err(p_dec, "Exception in MediaCodec.getOutputFormat (GetOutput)");
+                (*env)->ExceptionClear(env);
+                p_sys->error_state = true;
+                return;
+            }
+
             jobject format_string = (*env)->CallObjectMethod(env, format, p_sys->tostring);
 
             jsize format_len = (*env)->GetStringUTFLength(env, format_string);
@@ -783,8 +842,14 @@ static void GetOutput(decoder_t *p_dec, JNIEnv *env, picture_t **pp_pic, jlong t
                     sar_den = p_dec->fmt_in.video.i_sar_den;
                 }
                 jni_SetAndroidSurfaceSizeEnv(env, width, height, width, height, sar_num, sar_den);
-            } else
-                GetVlcChromaFormat(p_sys->pixel_format, &p_dec->fmt_out.i_codec, &name);
+            } else {
+                if (!GetVlcChromaFormat(p_sys->pixel_format,
+                                        &p_dec->fmt_out.i_codec, &name)) {
+                    msg_Err(p_dec, "color-format not recognized");
+                    p_sys->error_state = true;
+                    return;
+                }
+            }
 
             msg_Dbg(p_dec, "output: %d %s, %dx%d stride %d %d, crop %d %d %d %d",
                     p_sys->pixel_format, name, width, height, p_sys->stride, p_sys->slice_height,
@@ -842,7 +907,7 @@ static picture_t *DecodeVideo(decoder_t *p_dec, block_t **pp_block)
         return NULL;
     }
 
-    (*myVm)->AttachCurrentThread(myVm, &env, NULL);
+    jni_attach_thread(&env, THREAD_NAME);
 
     if (p_block->i_flags & (BLOCK_FLAG_DISCONTINUITY|BLOCK_FLAG_CORRUPTED)) {
         block_Release(p_block);
@@ -862,7 +927,7 @@ static picture_t *DecodeVideo(decoder_t *p_dec, block_t **pp_block)
             }
         }
         p_sys->decoded = false;
-        (*myVm)->DetachCurrentThread(myVm);
+        jni_detach_thread();
         return NULL;
     }
 
@@ -880,6 +945,7 @@ static picture_t *DecodeVideo(decoder_t *p_dec, block_t **pp_block)
     while (true) {
         int index = (*env)->CallIntMethod(env, p_sys->codec, p_sys->dequeue_input_buffer, (jlong) 0);
         if ((*env)->ExceptionOccurred(env)) {
+            msg_Err(p_dec, "Exception occurred in MediaCodec.dequeueInputBuffer");
             (*env)->ExceptionClear(env);
             p_sys->error_state = true;
             break;
@@ -887,13 +953,15 @@ static picture_t *DecodeVideo(decoder_t *p_dec, block_t **pp_block)
 
         if (index < 0) {
             GetOutput(p_dec, env, &p_pic, timeout);
+            if (p_sys->error_state)
+                break;
             if (p_pic) {
                 /* If we couldn't get an available input buffer but a
                  * decoded frame is available, we return the frame
                  * without assigning NULL to *pp_block. The next call
                  * to DecodeVideo will try to send the input packet again.
                  */
-                (*myVm)->DetachCurrentThread(myVm);
+                jni_detach_thread();
                 return p_pic;
             }
             timeout = 30 * 1000;
@@ -919,7 +987,7 @@ static picture_t *DecodeVideo(decoder_t *p_dec, block_t **pp_block)
                     block_Release(p_block);
                     *pp_block = NULL;
                 }
-                (*myVm)->DetachCurrentThread(myVm);
+                jni_detach_thread();
                 return invalid_picture;
             }
             continue;
@@ -928,7 +996,12 @@ static picture_t *DecodeVideo(decoder_t *p_dec, block_t **pp_block)
         jobject buf = (*env)->GetObjectArrayElement(env, p_sys->input_buffers, index);
         jsize size = (*env)->GetDirectBufferCapacity(env, buf);
         uint8_t *bufptr = (*env)->GetDirectBufferAddress(env, buf);
-        if (size > p_block->i_buffer)
+        if (size < 0) {
+            msg_Err(p_dec, "Java buffer has invalid size");
+            p_sys->error_state = true;
+            break;
+        }
+        if ((size_t) size > p_block->i_buffer)
             size = p_block->i_buffer;
         memcpy(bufptr, p_block->p_buffer, size);
 
@@ -940,12 +1013,24 @@ static picture_t *DecodeVideo(decoder_t *p_dec, block_t **pp_block)
         timestamp_FifoPut(p_sys->timestamp_fifo, p_block->i_pts ? VLC_TS_INVALID : p_block->i_dts);
         (*env)->CallVoidMethod(env, p_sys->codec, p_sys->queue_input_buffer, index, 0, size, ts, 0);
         (*env)->DeleteLocalRef(env, buf);
+        if ((*env)->ExceptionOccurred(env)) {
+            msg_Err(p_dec, "Exception in MediaCodec.queueInputBuffer");
+            (*env)->ExceptionClear(env);
+            p_sys->error_state = true;
+            break;
+        }
         p_sys->decoded = true;
         break;
     }
+    if (p_sys->error_state) {
+        if (p_pic)
+            picture_Release(p_pic);
+        jni_detach_thread();
+        return NULL;
+    }
     if (!p_pic)
         GetOutput(p_dec, env, &p_pic, 0);
-    (*myVm)->DetachCurrentThread(myVm);
+    jni_detach_thread();
 
     block_Release(p_block);
     *pp_block = NULL;