static sdl_amedia_status_t SDL_AMediaCodecJava_configure_surface( JNIEnv*env, SDL_AMediaCodec* acodec, const SDL_AMediaFormat* aformat, jobject android_surface, SDL_AMediaCrypto *crypto, uint32_t flags) { SDLTRACE("SDL_AMediaCodecJava_configure_surface"); // TODO: implement SDL_AMediaCrypto SDL_AMediaCodec_Opaque *opaque = (SDL_AMediaCodec_Opaque *)acodec->opaque; jobject android_media_format = SDL_AMediaFormatJava_getObject(env, aformat); jobject android_media_codec = SDL_AMediaCodecJava_getObject(env, acodec); ALOGE("configure acodec:%p format:%p: surface:%p", android_media_codec, android_media_format, android_surface); (*env)->CallVoidMethod(env, android_media_codec, g_clazz.jmid_configure, android_media_format, android_surface, crypto, flags); if (SDL_JNI_CatchException(env)) { return SDL_AMEDIA_ERROR_UNKNOWN; } opaque->is_input_buffer_valid = true; SDL_JNI_DeleteGlobalRefP(env, &opaque->input_buffer_array); SDL_JNI_DeleteGlobalRefP(env, &opaque->output_buffer_array); return SDL_AMEDIA_OK; }
ssize_t SDL_AMediaCodecJava_dequeueOutputBuffer(SDL_AMediaCodec* acodec, SDL_AMediaCodecBufferInfo *info, int64_t timeoutUs) { AMCTRACE("%s(%d)", __func__, (int)timeoutUs); JNIEnv *env = NULL; if (JNI_OK != SDL_JNI_SetupThreadEnv(&env)) { ALOGE("%s: SetupThreadEnv failed", __func__); return AMEDIACODEC__UNKNOWN_ERROR; } SDL_AMediaCodec_Opaque *opaque = (SDL_AMediaCodec_Opaque *)acodec->opaque; jobject android_media_codec = opaque->android_media_codec; if (!opaque->output_buffer_info) { opaque->output_buffer_info = SDL_JNI_NewObjectAsGlobalRef(env, g_clazz_BufferInfo.clazz, g_clazz_BufferInfo.jmid__ctor); if (SDL_JNI_CatchException(env) || !opaque->output_buffer_info) { ALOGE("%s: SDL_JNI_NewObjectAsGlobalRef failed", __func__); return AMEDIACODEC__UNKNOWN_ERROR; } } jint idx = AMEDIACODEC__UNKNOWN_ERROR; while (1) { idx = (*env)->CallIntMethod(env, android_media_codec, g_clazz.jmid_dequeueOutputBuffer, opaque->output_buffer_info, (jlong)timeoutUs); if (SDL_JNI_CatchException(env)) { ALOGI("%s: Exception\n", __func__); return AMEDIACODEC__UNKNOWN_ERROR; } if (idx == AMEDIACODEC__INFO_OUTPUT_BUFFERS_CHANGED) { ALOGI("%s: INFO_OUTPUT_BUFFERS_CHANGED\n", __func__); SDL_JNI_DeleteGlobalRefP(env, &opaque->input_buffer_array); SDL_JNI_DeleteGlobalRefP(env, &opaque->output_buffer_array); continue; } else if (idx == AMEDIACODEC__INFO_OUTPUT_FORMAT_CHANGED) { ALOGI("%s: INFO_OUTPUT_FORMAT_CHANGED\n", __func__); } else if (idx >= 0) { AMCTRACE("%s: buffer ready (%d) ====================\n", __func__, idx); if (info) { info->offset = (*env)->GetIntField(env, opaque->output_buffer_info, g_clazz_BufferInfo.jfid_offset); info->size = (*env)->GetIntField(env, opaque->output_buffer_info, g_clazz_BufferInfo.jfid_size); info->presentationTimeUs = (*env)->GetLongField(env, opaque->output_buffer_info, g_clazz_BufferInfo.jfid_presentationTimeUs); info->flags = (*env)->GetIntField(env, opaque->output_buffer_info, g_clazz_BufferInfo.jfid_flags); } } break; } return idx; }
static void func_destroy(IJKFF_Pipenode *node) { if (!node || !node->opaque) return; IJKFF_Pipenode_Opaque *opaque = node->opaque; SDL_DestroyCondP(&opaque->acodec_cond); SDL_DestroyMutexP(&opaque->acodec_mutex); SDL_DestroyCondP(&opaque->acodec_first_dequeue_output_cond); SDL_DestroyMutexP(&opaque->acodec_first_dequeue_output_mutex); SDL_AMediaCodec_decreaseReferenceP(&opaque->acodec); SDL_AMediaFormat_deleteP(&opaque->input_aformat); SDL_AMediaFormat_deleteP(&opaque->output_aformat); #if AMC_USE_AVBITSTREAM_FILTER av_freep(&opaque->orig_extradata); #endif ffp_packet_queue_destroy(&opaque->fake_pictq); if (opaque->bsfc) { av_bitstream_filter_close(opaque->bsfc); opaque->bsfc = NULL; } JNIEnv *env = NULL; if (JNI_OK == SDL_JNI_SetupThreadEnv(&env)) { SDL_JNI_DeleteGlobalRefP(env, &opaque->jsurface); } }
int ffpipeline_set_surface(JNIEnv *env, IJKFF_Pipeline* pipeline, jobject surface) { ALOGD("%s()\n", __func__); if (!check_ffpipeline(pipeline, __func__)) return -1; IJKFF_Pipeline_Opaque *opaque = pipeline->opaque; if (!opaque->surface_mutex) return -1; SDL_LockMutex(opaque->surface_mutex); { jobject prev_surface = opaque->jsurface; if ((surface == prev_surface) || (surface && prev_surface && (*env)->IsSameObject(env, surface, prev_surface))) { // same object, no need to reconfigure } else { SDL_VoutAndroid_setAMediaCodec(opaque->weak_vout, NULL); if (surface) { opaque->jsurface = (*env)->NewGlobalRef(env, surface); } else { opaque->jsurface = NULL; } opaque->is_surface_need_reconfigure = true; if (prev_surface != NULL) { SDL_JNI_DeleteGlobalRefP(env, &prev_surface); } } } SDL_UnlockMutex(opaque->surface_mutex); return 0; }
static int reconfigure_codec_l(JNIEnv *env, IJKFF_Pipenode *node) { IJKFF_Pipenode_Opaque *opaque = node->opaque; IJKFF_Pipeline *pipeline = opaque->pipeline; int ret = 0; sdl_amedia_status_t amc_ret = 0; jobject prev_jsurface = NULL; ffpipeline_set_surface_need_reconfigure_l(pipeline, false); prev_jsurface = opaque->jsurface; opaque->jsurface = ffpipeline_get_surface_as_global_ref_l(env, pipeline); SDL_JNI_DeleteGlobalRefP(env, &prev_jsurface); if (!opaque->acodec) { opaque->acodec = create_codec_l(env, node); if (!opaque->acodec) { ALOGE("%s:open_video_decoder: create_codec failed\n", __func__); ret = -1; goto fail; } } if (SDL_AMediaCodec_isConfigured(opaque->acodec)) { if (opaque->acodec) { if (SDL_AMediaCodec_isStarted(opaque->acodec)) { SDL_VoutAndroid_invalidateAllBuffers(opaque->weak_vout); SDL_AMediaCodec_stop(opaque->acodec); } if (opaque->quirk_reconfigure_with_new_codec) { ALOGI("quirk: reconfigure with new codec"); SDL_AMediaCodec_decreaseReferenceP(&opaque->acodec); opaque->acodec = create_codec_l(env, node); if (!opaque->acodec) { ALOGE("%s:open_video_decoder: create_codec failed\n", __func__); ret = -1; goto fail; } } } assert(opaque->weak_vout); } amc_ret = SDL_AMediaCodec_configure_surface(env, opaque->acodec, opaque->input_aformat, opaque->jsurface, NULL, 0); if (amc_ret != SDL_AMEDIA_OK) { ALOGE("%s:configure_surface: failed\n", __func__); ret = -1; goto fail; } SDL_AMediaCodec_start(opaque->acodec); opaque->acodec_first_dequeue_output_request = true; ALOGI("%s:new acodec: %p\n", __func__, opaque->acodec); SDL_VoutAndroid_setAMediaCodec(opaque->weak_vout, opaque->acodec); fail: return ret; }
static sdl_amedia_status_t SDL_AMediaFormatJava_delete(SDL_AMediaFormat* aformat) { if (!aformat) return SDL_AMEDIA_OK; JNIEnv *env = NULL; if (JNI_OK != SDL_JNI_SetupThreadEnv(&env)) { ALOGE("%s: SetupThreadEnv failed", __func__); return SDL_AMEDIA_ERROR_UNKNOWN; } SDL_AMediaFormat_Opaque *opaque = (SDL_AMediaFormat_Opaque *)aformat->opaque; if (opaque) { SDL_JNI_DeleteGlobalRefP(env, &opaque->android_byte_buffer); SDL_JNI_DeleteGlobalRefP(env, &opaque->android_media_format); } SDL_AMediaFormat_FreeInternal(aformat); return SDL_AMEDIA_OK; }
static sdl_amedia_status_t SDL_AMediaCodecJava_delete(SDL_AMediaCodec* acodec) { ALOGI("%s\n", __func__); if (!acodec) return SDL_AMEDIA_OK; JNIEnv *env = NULL; if (JNI_OK != SDL_JNI_SetupThreadEnv(&env)) { ALOGE("SDL_AMediaCodecJava_delete: SetupThreadEnv failed"); return SDL_AMEDIA_ERROR_UNKNOWN; } SDL_AMediaCodec_Opaque *opaque = (SDL_AMediaCodec_Opaque *)acodec->opaque; if (opaque) { if (opaque->android_media_codec) { (*env)->CallVoidMethod(env, opaque->android_media_codec, g_clazz.jmid_release); SDL_JNI_CatchException(env); } SDL_JNI_DeleteGlobalRefP(env, &opaque->output_buffer_info); SDL_JNI_DeleteGlobalRefP(env, &opaque->output_buffer); SDL_JNI_DeleteGlobalRefP(env, &opaque->output_buffer_array); SDL_JNI_DeleteGlobalRefP(env, &opaque->input_buffer); SDL_JNI_DeleteGlobalRefP(env, &opaque->input_buffer_array); SDL_JNI_DeleteGlobalRefP(env, &opaque->android_media_codec); } SDL_AMediaCodec_FreeInternal(acodec); return SDL_AMEDIA_OK; }
static void func_destroy(IJKFF_Pipeline *pipeline) { IJKFF_Pipeline_Opaque *opaque = pipeline->opaque; JNIEnv *env = NULL; SDL_DestroyMutexP(&opaque->surface_mutex); if (JNI_OK != SDL_JNI_SetupThreadEnv(&env)) { ALOGE("amediacodec-pipeline:destroy: SetupThreadEnv failed\n"); goto fail; } SDL_JNI_DeleteGlobalRefP(env, &opaque->jsurface); fail: return; }
SDL_AMediaFormat *SDL_AMediaFormatJava_init(JNIEnv *env, jobject android_format) { SDLTRACE("%s", __func__); jobject global_android_media_format = (*env)->NewGlobalRef(env, android_format); if (J4A_ExceptionCheck__catchAll(env) || !global_android_media_format) { return NULL; } SDL_AMediaFormat *aformat = SDL_AMediaFormat_CreateInternal(sizeof(SDL_AMediaFormat_Opaque)); if (!aformat) { SDL_JNI_DeleteGlobalRefP(env, &global_android_media_format); return NULL; } setup_aformat(aformat, global_android_media_format); return aformat; }
SDL_AMediaFormat *SDL_AMediaFormatJava_createVideoFormat(JNIEnv *env, const char *mime, int width, int height) { SDLTRACE("%s", __func__); jobject android_media_format = J4AC_MediaFormat__createVideoFormat__withCString__asGlobalRef__catchAll(env, mime, width, height); if (J4A_ExceptionCheck__catchAll(env) || !android_media_format) { return NULL; } SDL_AMediaFormat *aformat = SDL_AMediaFormat_CreateInternal(sizeof(SDL_AMediaFormat_Opaque)); if (!aformat) { SDL_JNI_DeleteGlobalRefP(env, &android_media_format); return NULL; } setup_aformat(aformat, android_media_format); SDL_AMediaFormat_setInt32(aformat, AMEDIAFORMAT_KEY_MAX_INPUT_SIZE, 0); return aformat; }
static SDL_AMediaCodec* SDL_AMediaCodecJava_init(JNIEnv *env, jobject android_media_codec) { SDLTRACE("%s", __func__); jobject global_android_media_codec = (*env)->NewGlobalRef(env, android_media_codec); if (SDL_JNI_CatchException(env) || !global_android_media_codec) { return NULL; } SDL_AMediaCodec *acodec = SDL_AMediaCodec_CreateInternal(sizeof(SDL_AMediaCodec_Opaque)); if (!acodec) { SDL_JNI_DeleteGlobalRefP(env, &global_android_media_codec); return NULL; } SDL_AMediaCodec_Opaque *opaque = acodec->opaque; opaque->android_media_codec = global_android_media_codec; acodec->opaque_class = &g_amediacodec_class; acodec->func_delete = SDL_AMediaCodecJava_delete; acodec->func_configure = NULL; acodec->func_configure_surface = SDL_AMediaCodecJava_configure_surface; acodec->func_start = SDL_AMediaCodecJava_start; acodec->func_stop = SDL_AMediaCodecJava_stop; acodec->func_flush = SDL_AMediaCodecJava_flush; acodec->func_getInputBuffer = SDL_AMediaCodecJava_getInputBuffer; acodec->func_getOutputBuffer = SDL_AMediaCodecJava_getOutputBuffer; acodec->func_dequeueInputBuffer = SDL_AMediaCodecJava_dequeueInputBuffer; acodec->func_queueInputBuffer = SDL_AMediaCodecJava_queueInputBuffer; acodec->func_dequeueOutputBuffer = SDL_AMediaCodecJava_dequeueOutputBuffer; acodec->func_getOutputFormat = SDL_AMediaCodecJava_getOutputFormat; acodec->func_releaseOutputBuffer = SDL_AMediaCodecJava_releaseOutputBuffer; acodec->func_isInputBuffersValid = SDL_AMediaCodecJava_isInputBuffersValid; SDL_AMediaCodec_increaseReference(acodec); return acodec; }
static uint8_t* SDL_AMediaCodecJava_getInputBuffer(SDL_AMediaCodec* acodec, size_t idx, size_t *out_size) { AMCTRACE("%s", __func__); JNIEnv *env = NULL; if (JNI_OK != SDL_JNI_SetupThreadEnv(&env)) { ALOGE("%s: SetupThreadEnv failed", __func__); return NULL; } SDL_AMediaCodec_Opaque *opaque = (SDL_AMediaCodec_Opaque *)acodec->opaque; if (0 != getInputBuffers(env, acodec)) return NULL; assert(opaque->input_buffer_array); int buffer_count = (*env)->GetArrayLength(env, opaque->input_buffer_array); if (SDL_JNI_CatchException(env) || idx < 0 || idx >= buffer_count) { ALOGE("%s: idx(%d) < count(%d)\n", __func__, (int)idx, (int)buffer_count); return NULL; } SDL_JNI_DeleteGlobalRefP(env, &opaque->input_buffer); jobject local_input_buffer = (*env)->GetObjectArrayElement(env, opaque->input_buffer_array, idx); if (SDL_JNI_CatchException(env) || !local_input_buffer) { ALOGE("%s: GetObjectArrayElement failed\n", __func__); return NULL; } opaque->input_buffer = (*env)->NewGlobalRef(env, local_input_buffer); SDL_JNI_DeleteLocalRefP(env, &local_input_buffer); if (SDL_JNI_CatchException(env) || !opaque->input_buffer) { ALOGE("%s: GetObjectArrayElement.NewGlobalRef failed\n", __func__); return NULL; } jlong size = (*env)->GetDirectBufferCapacity(env, opaque->input_buffer); void *ptr = (*env)->GetDirectBufferAddress(env, opaque->input_buffer); if (out_size) *out_size = size; return ptr; }
inline static int getOutputBuffers(JNIEnv *env, SDL_AMediaCodec* acodec) { SDL_AMediaCodec_Opaque *opaque = (SDL_AMediaCodec_Opaque *)acodec->opaque; jobject android_media_codec = opaque->android_media_codec; SDL_JNI_DeleteGlobalRefP(env, &opaque->output_buffer_array); if (opaque->output_buffer_array) return 0; jobjectArray local_output_buffer_array = (*env)->CallObjectMethod(env, android_media_codec, g_clazz.jmid_getOutputBuffers); if (SDL_JNI_CatchException(env) || !local_output_buffer_array) { ALOGE("%s: getInputBuffers failed\n", __func__); return -1; } opaque->output_buffer_array = (*env)->NewGlobalRef(env, local_output_buffer_array); SDL_JNI_DeleteLocalRefP(env, &local_output_buffer_array); if (SDL_JNI_CatchException(env) || !opaque->output_buffer_array) { ALOGE("%s: getOutputBuffers.NewGlobalRef failed\n", __func__); return -1; } return 0; }