JNIEXPORT void JNICALL Java_org_linphone_mediastream_video_capture_AndroidVideoApi5JniWrapper_putImage(JNIEnv* env, jclass thiz,jlong nativePtr,jbyteArray frame) { AndroidReaderContext* d = (AndroidReaderContext*) nativePtr; if (!d->androidCamera) return; ms_mutex_lock(&d->mutex); if (!ms_video_capture_new_frame(&d->fpsControl,d->filter->ticker->time)) { ms_mutex_unlock(&d->mutex); return; } if (d->rotation != UNDEFINED_ROTATION && d->rotationSavedDuringVSize != d->rotation) { ms_warning("Rotation has changed (new value: %d) since vsize was run (old value: %d)." "Will produce inverted images. Use set_device_orientation() then update call.\n", d->rotation, d->rotationSavedDuringVSize); } int image_rotation_correction = compute_image_rotation_correction(d, d->rotationSavedDuringVSize); jboolean isCopied; jbyte* jinternal_buff = env->GetByteArrayElements(frame, &isCopied); if (isCopied) { ms_warning("The video frame received from Java has been copied"); } int y_cropping_offset=0, cbcr_cropping_offset=0; //compute_cropping_offsets(d->hwCapableSize, d->requestedSize, &y_cropping_offset, &cbcr_cropping_offset); int width = d->hwCapableSize.width; int height = d->hwCapableSize.height; uint8_t* y_src = (uint8_t*)(jinternal_buff + y_cropping_offset); uint8_t* cbcr_src = (uint8_t*) (jinternal_buff + width * height + cbcr_cropping_offset); /* Warning note: image_rotation_correction == 90 does not imply portrait mode ! (incorrect function naming). It only implies one thing: image needs to rotated by that amount to be correctly displayed. */ mblk_t* yuv_block = copy_ycbcrbiplanar_to_true_yuv_with_rotation_and_down_scale_by_2(y_src , cbcr_src , image_rotation_correction , d->requestedSize.width , d->requestedSize.height , d->hwCapableSize.width , d->hwCapableSize.width, false, d->useDownscaling); if (yuv_block) { if (d->frame) freemsg(d->frame); d->frame = yuv_block; } ms_mutex_unlock(&d->mutex); // JNI_ABORT free the buffer without copying back the possible changes env->ReleaseByteArrayElements(frame, jinternal_buff, JNI_ABORT); }
static int video_capture_set_vsize(MSFilter *f, void* data){ AndroidReaderContext* d = (AndroidReaderContext*) f->data; ms_mutex_lock(&d->mutex); d->requestedSize=*(MSVideoSize*)data; // always request landscape mode, orientation is handled later if (d->requestedSize.height > d->requestedSize.width) { int tmp = d->requestedSize.height; d->requestedSize.height = d->requestedSize.width; d->requestedSize.width = tmp; } JNIEnv *env = ms_get_jni_env(); jmethodID method = env->GetStaticMethodID(d->helperClass,"selectNearestResolutionAvailable", "(III)[I"); // find neareast hw-available resolution (using jni call); jobject resArray = env->CallStaticObjectMethod(d->helperClass, method, ((AndroidWebcamConfig*)d->webcam->data)->id, d->requestedSize.width, d->requestedSize.height); if (!resArray) { ms_mutex_unlock(&d->mutex); ms_error("Failed to retrieve camera '%d' supported resolutions\n", ((AndroidWebcamConfig*)d->webcam->data)->id); return -1; } // handle result : // - 0 : width // - 1 : height // - 2 : useDownscaling jint res[3]; env->GetIntArrayRegion((jintArray)resArray, 0, 3, res); ms_message("Camera selected resolution is: %dx%d (requested: %dx%d) with downscaling?%d\n", res[0], res[1], d->requestedSize.width, d->requestedSize.height, res[2]); d->hwCapableSize.width = res[0]; d->hwCapableSize.height = res[1]; d->useDownscaling = res[2]; int rqSize = d->requestedSize.width * d->requestedSize.height; int hwSize = d->hwCapableSize.width * d->hwCapableSize.height; double downscale = d->useDownscaling ? 0.5 : 1; // if hw supplies a smaller resolution, modify requested size accordingly if ((hwSize * downscale * downscale) < rqSize) { ms_message("Camera cannot produce requested resolution %dx%d, will supply smaller one: %dx%d\n", d->requestedSize.width, d->requestedSize.height, (int) (res[0] * downscale), (int) (res[1]*downscale)); d->usedSize.width = (int) (d->hwCapableSize.width * downscale); d->usedSize.height = (int) (d->hwCapableSize.height * downscale); } else if ((hwSize * downscale * downscale) > rqSize) { ms_message("Camera cannot produce requested resolution %dx%d, will capture a bigger one (%dx%d) and crop it to match encoder requested resolution\n", d->requestedSize.width, d->requestedSize.height, (int)(res[0] * downscale), (int)(res[1] * downscale)); d->usedSize.width = d->requestedSize.width; d->usedSize.height = d->requestedSize.height; } else { d->usedSize.width = d->requestedSize.width; d->usedSize.height = d->requestedSize.height; } // is phone held |_ to cam orientation ? if (d->rotation == UNDEFINED_ROTATION || compute_image_rotation_correction(d, d->rotation) % 180 != 0) { if (d->rotation == UNDEFINED_ROTATION) { ms_error("To produce a correct image, Mediastreamer MUST be aware of device's orientation BEFORE calling 'configure_video_source'\n"); ms_warning("Capture filter do not know yet about device's orientation.\n" "Current assumption: device is held perpendicular to its webcam (ie: portrait mode for a phone)\n"); d->rotationSavedDuringVSize = 0; } else { d->rotationSavedDuringVSize = d->rotation; } bool camIsLandscape = d->hwCapableSize.width > d->hwCapableSize.height; bool useIsLandscape = d->usedSize.width > d->usedSize.height; // if both are landscape or both portrait, swap if (camIsLandscape == useIsLandscape) { int t = d->usedSize.width; d->usedSize.width = d->usedSize.height; d->usedSize.height = t; ms_message("Swapped resolution width and height to : %dx%d\n", d->usedSize.width, d->usedSize.height); } } else { d->rotationSavedDuringVSize = d->rotation; } ms_mutex_unlock(&d->mutex); return 0; }