| Index: media/capture/video/android/video_capture_device_android.cc
|
| diff --git a/media/capture/video/android/video_capture_device_android.cc b/media/capture/video/android/video_capture_device_android.cc
|
| index e388e9085140e6d35bc3e5db9808acc9627f2f32..3d0774c2307a907b8e3858b12e3c1e012d02232f 100644
|
| --- a/media/capture/video/android/video_capture_device_android.cc
|
| +++ b/media/capture/video/android/video_capture_device_android.cc
|
| @@ -12,7 +12,6 @@
|
| #include "base/android/jni_string.h"
|
| #include "base/numerics/safe_conversions.h"
|
| #include "base/strings/string_number_conversions.h"
|
| -#include "base/threading/thread_task_runner_handle.h"
|
| #include "jni/VideoCapture_jni.h"
|
| #include "media/capture/video/android/photo_capabilities.h"
|
| #include "media/capture/video/android/video_capture_device_factory_android.h"
|
| @@ -34,14 +33,12 @@
|
|
|
| VideoCaptureDeviceAndroid::VideoCaptureDeviceAndroid(
|
| const VideoCaptureDeviceDescriptor& device_descriptor)
|
| - : main_task_runner_(base::ThreadTaskRunnerHandle::Get()),
|
| - state_(kIdle),
|
| + : state_(kIdle),
|
| got_first_frame_(false),
|
| - device_descriptor_(device_descriptor),
|
| - weak_ptr_factory_(this) {}
|
| + device_descriptor_(device_descriptor) {}
|
|
|
| VideoCaptureDeviceAndroid::~VideoCaptureDeviceAndroid() {
|
| - DCHECK(main_task_runner_->BelongsToCurrentThread());
|
| + DCHECK(thread_checker_.CalledOnValidThread());
|
| StopAndDeAllocate();
|
| }
|
|
|
| @@ -58,7 +55,7 @@
|
| void VideoCaptureDeviceAndroid::AllocateAndStart(
|
| const VideoCaptureParams& params,
|
| std::unique_ptr<Client> client) {
|
| - DCHECK(main_task_runner_->BelongsToCurrentThread());
|
| + DCHECK(thread_checker_.CalledOnValidThread());
|
| {
|
| base::AutoLock lock(lock_);
|
| if (state_ != kIdle)
|
| @@ -107,15 +104,15 @@
|
|
|
| {
|
| base::AutoLock lock(lock_);
|
| - state_ = kConfigured;
|
| + state_ = kCapturing;
|
| }
|
| }
|
|
|
| void VideoCaptureDeviceAndroid::StopAndDeAllocate() {
|
| - DCHECK(main_task_runner_->BelongsToCurrentThread());
|
| - {
|
| - base::AutoLock lock(lock_);
|
| - if (state_ != kConfigured && state_ != kError)
|
| + DCHECK(thread_checker_.CalledOnValidThread());
|
| + {
|
| + base::AutoLock lock(lock_);
|
| + if (state_ != kCapturing && state_ != kError)
|
| return;
|
| }
|
|
|
| @@ -137,251 +134,13 @@
|
| }
|
|
|
| void VideoCaptureDeviceAndroid::TakePhoto(TakePhotoCallback callback) {
|
| - DCHECK(main_task_runner_->BelongsToCurrentThread());
|
| - {
|
| - base::AutoLock lock(lock_);
|
| - if (state_ != kConfigured)
|
| - return;
|
| - if (!got_first_frame_) { // We have to wait until we get the first frame.
|
| - photo_requests_queue_.push_back(
|
| - base::Bind(&VideoCaptureDeviceAndroid::DoTakePhoto,
|
| - weak_ptr_factory_.GetWeakPtr(), base::Passed(&callback)));
|
| - return;
|
| - }
|
| - }
|
| - DoTakePhoto(std::move(callback));
|
| -}
|
| -
|
| -void VideoCaptureDeviceAndroid::GetPhotoCapabilities(
|
| - GetPhotoCapabilitiesCallback callback) {
|
| - DCHECK(main_task_runner_->BelongsToCurrentThread());
|
| - {
|
| - base::AutoLock lock(lock_);
|
| - if (state_ != kConfigured)
|
| - return;
|
| - if (!got_first_frame_) { // We have to wait until we get the first frame.
|
| - photo_requests_queue_.push_back(
|
| - base::Bind(&VideoCaptureDeviceAndroid::DoGetPhotoCapabilities,
|
| - weak_ptr_factory_.GetWeakPtr(), base::Passed(&callback)));
|
| - return;
|
| - }
|
| - }
|
| - DoGetPhotoCapabilities(std::move(callback));
|
| -}
|
| -
|
| -void VideoCaptureDeviceAndroid::SetPhotoOptions(
|
| - mojom::PhotoSettingsPtr settings,
|
| - SetPhotoOptionsCallback callback) {
|
| - DCHECK(main_task_runner_->BelongsToCurrentThread());
|
| - {
|
| - base::AutoLock lock(lock_);
|
| - if (state_ != kConfigured)
|
| - return;
|
| - if (!got_first_frame_) { // We have to wait until we get the first frame.
|
| - photo_requests_queue_.push_back(
|
| - base::Bind(&VideoCaptureDeviceAndroid::DoSetPhotoOptions,
|
| - weak_ptr_factory_.GetWeakPtr(), base::Passed(&settings),
|
| - base::Passed(&callback)));
|
| - return;
|
| - }
|
| - }
|
| - DoSetPhotoOptions(std::move(settings), std::move(callback));
|
| -}
|
| -
|
| -void VideoCaptureDeviceAndroid::OnFrameAvailable(
|
| - JNIEnv* env,
|
| - const JavaParamRef<jobject>& obj,
|
| - const JavaParamRef<jbyteArray>& data,
|
| - jint length,
|
| - jint rotation) {
|
| - {
|
| - base::AutoLock lock(lock_);
|
| - if (state_ != kConfigured || !client_)
|
| - return;
|
| - }
|
| -
|
| - jbyte* buffer = env->GetByteArrayElements(data, NULL);
|
| - if (!buffer) {
|
| - LOG(ERROR) << "VideoCaptureDeviceAndroid::OnFrameAvailable: "
|
| - "failed to GetByteArrayElements";
|
| - return;
|
| - }
|
| -
|
| - const base::TimeTicks current_time = base::TimeTicks::Now();
|
| - {
|
| - base::AutoLock lock(lock_);
|
| - if (!got_first_frame_) {
|
| - // Set aside one frame allowance for fluctuation.
|
| - expected_next_frame_time_ = current_time - frame_interval_;
|
| - first_ref_time_ = current_time;
|
| - got_first_frame_ = true;
|
| -
|
| - for (const auto& request : photo_requests_queue_)
|
| - main_task_runner_->PostTask(FROM_HERE, request);
|
| - photo_requests_queue_.clear();
|
| - }
|
| - }
|
| -
|
| - // Deliver the frame when it doesn't arrive too early.
|
| - if (expected_next_frame_time_ <= current_time) {
|
| - expected_next_frame_time_ += frame_interval_;
|
| -
|
| - // TODO(qiangchen): Investigate how to get raw timestamp for Android,
|
| - // rather than using reference time to calculate timestamp.
|
| - base::AutoLock lock(lock_);
|
| - if (!client_)
|
| - return;
|
| - client_->OnIncomingCapturedData(reinterpret_cast<uint8_t*>(buffer), length,
|
| - capture_format_, rotation, current_time,
|
| - current_time - first_ref_time_);
|
| - }
|
| -
|
| - env->ReleaseByteArrayElements(data, buffer, JNI_ABORT);
|
| -}
|
| -
|
| -void VideoCaptureDeviceAndroid::OnI420FrameAvailable(JNIEnv* env,
|
| - jobject obj,
|
| - jobject y_buffer,
|
| - jint y_stride,
|
| - jobject u_buffer,
|
| - jobject v_buffer,
|
| - jint uv_row_stride,
|
| - jint uv_pixel_stride,
|
| - jint width,
|
| - jint height,
|
| - jint rotation) {
|
| - {
|
| - base::AutoLock lock(lock_);
|
| - if (state_ != kConfigured || !client_)
|
| - return;
|
| - }
|
| -
|
| - const base::TimeTicks current_time = base::TimeTicks::Now();
|
| - {
|
| - base::AutoLock lock(lock_);
|
| - if (!got_first_frame_) {
|
| - // Set aside one frame allowance for fluctuation.
|
| - expected_next_frame_time_ = current_time - frame_interval_;
|
| - first_ref_time_ = current_time;
|
| - got_first_frame_ = true;
|
| -
|
| - for (const auto& request : photo_requests_queue_)
|
| - main_task_runner_->PostTask(FROM_HERE, request);
|
| - photo_requests_queue_.clear();
|
| - }
|
| - }
|
| -
|
| - uint8_t* const y_src =
|
| - reinterpret_cast<uint8_t*>(env->GetDirectBufferAddress(y_buffer));
|
| - CHECK(y_src);
|
| - uint8_t* const u_src =
|
| - reinterpret_cast<uint8_t*>(env->GetDirectBufferAddress(u_buffer));
|
| - CHECK(u_src);
|
| - uint8_t* const v_src =
|
| - reinterpret_cast<uint8_t*>(env->GetDirectBufferAddress(v_buffer));
|
| - CHECK(v_src);
|
| -
|
| - const int y_plane_length = width * height;
|
| - const int uv_plane_length = y_plane_length / 4;
|
| - const int buffer_length = y_plane_length + uv_plane_length * 2;
|
| - std::unique_ptr<uint8_t> buffer(new uint8_t[buffer_length]);
|
| -
|
| - libyuv::Android420ToI420(y_src, y_stride, u_src, uv_row_stride, v_src,
|
| - uv_row_stride, uv_pixel_stride, buffer.get(), width,
|
| - buffer.get() + y_plane_length, width / 2,
|
| - buffer.get() + y_plane_length + uv_plane_length,
|
| - width / 2, width, height);
|
| -
|
| - // Deliver the frame when it doesn't arrive too early.
|
| - if (expected_next_frame_time_ <= current_time) {
|
| - expected_next_frame_time_ += frame_interval_;
|
| -
|
| - // TODO(qiangchen): Investigate how to get raw timestamp for Android,
|
| - // rather than using reference time to calculate timestamp.
|
| - base::AutoLock lock(lock_);
|
| - if (!client_)
|
| - return;
|
| - client_->OnIncomingCapturedData(buffer.get(), buffer_length,
|
| - capture_format_, rotation, current_time,
|
| - current_time - first_ref_time_);
|
| - }
|
| -}
|
| -
|
| -void VideoCaptureDeviceAndroid::OnError(JNIEnv* env,
|
| - const JavaParamRef<jobject>& obj,
|
| - const JavaParamRef<jstring>& message) {
|
| - SetErrorState(FROM_HERE,
|
| - base::android::ConvertJavaStringToUTF8(env, message));
|
| -}
|
| -
|
| -void VideoCaptureDeviceAndroid::OnPhotoTaken(
|
| - JNIEnv* env,
|
| - const base::android::JavaParamRef<jobject>& obj,
|
| - jlong callback_id,
|
| - const base::android::JavaParamRef<jbyteArray>& data) {
|
| - DCHECK(callback_id);
|
| -
|
| - base::AutoLock lock(photo_callbacks_lock_);
|
| -
|
| - TakePhotoCallback* const cb =
|
| - reinterpret_cast<TakePhotoCallback*>(callback_id);
|
| - // Search for the pointer |cb| in the list of |photo_callbacks_|.
|
| - const auto reference_it =
|
| - std::find_if(photo_callbacks_.begin(), photo_callbacks_.end(),
|
| - [cb](const std::unique_ptr<TakePhotoCallback>& callback) {
|
| - return callback.get() == cb;
|
| - });
|
| - if (reference_it == photo_callbacks_.end()) {
|
| - NOTREACHED() << "|callback_id| not found.";
|
| - return;
|
| - }
|
| -
|
| - mojom::BlobPtr blob = mojom::Blob::New();
|
| - base::android::JavaByteArrayToByteVector(env, data.obj(), &blob->data);
|
| - blob->mime_type = blob->data.empty() ? "" : "image/jpeg";
|
| - cb->Run(std::move(blob));
|
| -
|
| - photo_callbacks_.erase(reference_it);
|
| -}
|
| -
|
| -VideoPixelFormat VideoCaptureDeviceAndroid::GetColorspace() {
|
| - JNIEnv* env = AttachCurrentThread();
|
| - const int current_capture_colorspace =
|
| - Java_VideoCapture_getColorspace(env, j_capture_.obj());
|
| - switch (current_capture_colorspace) {
|
| - case ANDROID_IMAGE_FORMAT_YV12:
|
| - return media::PIXEL_FORMAT_YV12;
|
| - case ANDROID_IMAGE_FORMAT_YUV_420_888:
|
| - return media::PIXEL_FORMAT_I420;
|
| - case ANDROID_IMAGE_FORMAT_NV21:
|
| - return media::PIXEL_FORMAT_NV21;
|
| - case ANDROID_IMAGE_FORMAT_UNKNOWN:
|
| - default:
|
| - return media::PIXEL_FORMAT_UNKNOWN;
|
| - }
|
| -}
|
| -
|
| -void VideoCaptureDeviceAndroid::SetErrorState(
|
| - const tracked_objects::Location& from_here,
|
| - const std::string& reason) {
|
| - {
|
| - base::AutoLock lock(lock_);
|
| - state_ = kError;
|
| - if (!client_)
|
| - return;
|
| - client_->OnError(from_here, reason);
|
| - }
|
| -}
|
| -
|
| -void VideoCaptureDeviceAndroid::DoTakePhoto(TakePhotoCallback callback) {
|
| - DCHECK(main_task_runner_->BelongsToCurrentThread());
|
| -#if DCHECK_IS_ON()
|
| - {
|
| - base::AutoLock lock(lock_);
|
| - DCHECK_EQ(kConfigured, state_);
|
| - DCHECK(got_first_frame_);
|
| - }
|
| -#endif
|
| + DCHECK(thread_checker_.CalledOnValidThread());
|
| + {
|
| + base::AutoLock lock(lock_);
|
| + if (state_ != kCapturing)
|
| + return;
|
| + }
|
| +
|
| JNIEnv* env = AttachCurrentThread();
|
|
|
| // Make copy on the heap so we can pass the pointer through JNI.
|
| @@ -399,16 +158,9 @@
|
| }
|
| }
|
|
|
| -void VideoCaptureDeviceAndroid::DoGetPhotoCapabilities(
|
| +void VideoCaptureDeviceAndroid::GetPhotoCapabilities(
|
| GetPhotoCapabilitiesCallback callback) {
|
| - DCHECK(main_task_runner_->BelongsToCurrentThread());
|
| -#if DCHECK_IS_ON()
|
| - {
|
| - base::AutoLock lock(lock_);
|
| - DCHECK_EQ(kConfigured, state_);
|
| - DCHECK(got_first_frame_);
|
| - }
|
| -#endif
|
| + DCHECK(thread_checker_.CalledOnValidThread());
|
| JNIEnv* env = AttachCurrentThread();
|
|
|
| PhotoCapabilities caps(
|
| @@ -440,19 +192,11 @@
|
| callback.Run(std::move(photo_capabilities));
|
| }
|
|
|
| -void VideoCaptureDeviceAndroid::DoSetPhotoOptions(
|
| +void VideoCaptureDeviceAndroid::SetPhotoOptions(
|
| mojom::PhotoSettingsPtr settings,
|
| SetPhotoOptionsCallback callback) {
|
| - DCHECK(main_task_runner_->BelongsToCurrentThread());
|
| -#if DCHECK_IS_ON()
|
| - {
|
| - base::AutoLock lock(lock_);
|
| - DCHECK_EQ(kConfigured, state_);
|
| - DCHECK(got_first_frame_);
|
| - }
|
| -#endif
|
| - JNIEnv* env = AttachCurrentThread();
|
| -
|
| + DCHECK(thread_checker_.CalledOnValidThread());
|
| + JNIEnv* env = AttachCurrentThread();
|
| // |width| and/or |height| are kept for the next TakePhoto()s.
|
| if (settings->has_width || settings->has_height)
|
| next_photo_resolution_.SetSize(0, 0);
|
| @@ -467,8 +211,178 @@
|
|
|
| if (settings->has_zoom)
|
| Java_VideoCapture_setZoom(env, j_capture_.obj(), settings->zoom);
|
| -
|
| callback.Run(true);
|
| }
|
|
|
| +void VideoCaptureDeviceAndroid::OnFrameAvailable(
|
| + JNIEnv* env,
|
| + const JavaParamRef<jobject>& obj,
|
| + const JavaParamRef<jbyteArray>& data,
|
| + jint length,
|
| + jint rotation) {
|
| + {
|
| + base::AutoLock lock(lock_);
|
| + if (state_ != kCapturing || !client_)
|
| + return;
|
| + }
|
| +
|
| + jbyte* buffer = env->GetByteArrayElements(data, NULL);
|
| + if (!buffer) {
|
| + LOG(ERROR) << "VideoCaptureDeviceAndroid::OnFrameAvailable: "
|
| + "failed to GetByteArrayElements";
|
| + return;
|
| + }
|
| +
|
| + const base::TimeTicks current_time = base::TimeTicks::Now();
|
| + if (!got_first_frame_) {
|
| + // Set aside one frame allowance for fluctuation.
|
| + expected_next_frame_time_ = current_time - frame_interval_;
|
| + first_ref_time_ = current_time;
|
| + got_first_frame_ = true;
|
| + }
|
| +
|
| + // Deliver the frame when it doesn't arrive too early.
|
| + if (expected_next_frame_time_ <= current_time) {
|
| + expected_next_frame_time_ += frame_interval_;
|
| +
|
| + // TODO(qiangchen): Investigate how to get raw timestamp for Android,
|
| + // rather than using reference time to calculate timestamp.
|
| + base::AutoLock lock(lock_);
|
| + if (!client_)
|
| + return;
|
| + client_->OnIncomingCapturedData(reinterpret_cast<uint8_t*>(buffer), length,
|
| + capture_format_, rotation, current_time,
|
| + current_time - first_ref_time_);
|
| + }
|
| +
|
| + env->ReleaseByteArrayElements(data, buffer, JNI_ABORT);
|
| +}
|
| +
|
| +void VideoCaptureDeviceAndroid::OnI420FrameAvailable(JNIEnv* env,
|
| + jobject obj,
|
| + jobject y_buffer,
|
| + jint y_stride,
|
| + jobject u_buffer,
|
| + jobject v_buffer,
|
| + jint uv_row_stride,
|
| + jint uv_pixel_stride,
|
| + jint width,
|
| + jint height,
|
| + jint rotation) {
|
| + {
|
| + base::AutoLock lock(lock_);
|
| + if (state_ != kCapturing || !client_)
|
| + return;
|
| + }
|
| +
|
| + const base::TimeTicks current_time = base::TimeTicks::Now();
|
| + if (!got_first_frame_) {
|
| + // Set aside one frame allowance for fluctuation.
|
| + expected_next_frame_time_ = current_time - frame_interval_;
|
| + first_ref_time_ = current_time;
|
| + got_first_frame_ = true;
|
| + }
|
| +
|
| + uint8_t* const y_src =
|
| + reinterpret_cast<uint8_t*>(env->GetDirectBufferAddress(y_buffer));
|
| + CHECK(y_src);
|
| + uint8_t* const u_src =
|
| + reinterpret_cast<uint8_t*>(env->GetDirectBufferAddress(u_buffer));
|
| + CHECK(u_src);
|
| + uint8_t* const v_src =
|
| + reinterpret_cast<uint8_t*>(env->GetDirectBufferAddress(v_buffer));
|
| + CHECK(v_src);
|
| +
|
| + const int y_plane_length = width * height;
|
| + const int uv_plane_length = y_plane_length / 4;
|
| + const int buffer_length = y_plane_length + uv_plane_length * 2;
|
| + std::unique_ptr<uint8_t> buffer(new uint8_t[buffer_length]);
|
| +
|
| + libyuv::Android420ToI420(y_src, y_stride, u_src, uv_row_stride, v_src,
|
| + uv_row_stride, uv_pixel_stride, buffer.get(), width,
|
| + buffer.get() + y_plane_length, width / 2,
|
| + buffer.get() + y_plane_length + uv_plane_length,
|
| + width / 2, width, height);
|
| +
|
| + // Deliver the frame when it doesn't arrive too early.
|
| + if (expected_next_frame_time_ <= current_time) {
|
| + expected_next_frame_time_ += frame_interval_;
|
| +
|
| + // TODO(qiangchen): Investigate how to get raw timestamp for Android,
|
| + // rather than using reference time to calculate timestamp.
|
| + base::AutoLock lock(lock_);
|
| + if (!client_)
|
| + return;
|
| + client_->OnIncomingCapturedData(buffer.get(), buffer_length,
|
| + capture_format_, rotation, current_time,
|
| + current_time - first_ref_time_);
|
| + }
|
| +}
|
| +
|
| +void VideoCaptureDeviceAndroid::OnError(JNIEnv* env,
|
| + const JavaParamRef<jobject>& obj,
|
| + const JavaParamRef<jstring>& message) {
|
| + SetErrorState(FROM_HERE,
|
| + base::android::ConvertJavaStringToUTF8(env, message));
|
| +}
|
| +
|
| +void VideoCaptureDeviceAndroid::OnPhotoTaken(
|
| + JNIEnv* env,
|
| + const base::android::JavaParamRef<jobject>& obj,
|
| + jlong callback_id,
|
| + const base::android::JavaParamRef<jbyteArray>& data) {
|
| + DCHECK(callback_id);
|
| +
|
| + base::AutoLock lock(photo_callbacks_lock_);
|
| +
|
| + TakePhotoCallback* const cb =
|
| + reinterpret_cast<TakePhotoCallback*>(callback_id);
|
| + // Search for the pointer |cb| in the list of |photo_callbacks_|.
|
| + const auto reference_it =
|
| + std::find_if(photo_callbacks_.begin(), photo_callbacks_.end(),
|
| + [cb](const std::unique_ptr<TakePhotoCallback>& callback) {
|
| + return callback.get() == cb;
|
| + });
|
| + if (reference_it == photo_callbacks_.end()) {
|
| + NOTREACHED() << "|callback_id| not found.";
|
| + return;
|
| + }
|
| +
|
| + mojom::BlobPtr blob = mojom::Blob::New();
|
| + base::android::JavaByteArrayToByteVector(env, data.obj(), &blob->data);
|
| + blob->mime_type = blob->data.empty() ? "" : "image/jpeg";
|
| + cb->Run(std::move(blob));
|
| +
|
| + photo_callbacks_.erase(reference_it);
|
| +}
|
| +
|
| +VideoPixelFormat VideoCaptureDeviceAndroid::GetColorspace() {
|
| + JNIEnv* env = AttachCurrentThread();
|
| + const int current_capture_colorspace =
|
| + Java_VideoCapture_getColorspace(env, j_capture_.obj());
|
| + switch (current_capture_colorspace) {
|
| + case ANDROID_IMAGE_FORMAT_YV12:
|
| + return media::PIXEL_FORMAT_YV12;
|
| + case ANDROID_IMAGE_FORMAT_YUV_420_888:
|
| + return media::PIXEL_FORMAT_I420;
|
| + case ANDROID_IMAGE_FORMAT_NV21:
|
| + return media::PIXEL_FORMAT_NV21;
|
| + case ANDROID_IMAGE_FORMAT_UNKNOWN:
|
| + default:
|
| + return media::PIXEL_FORMAT_UNKNOWN;
|
| + }
|
| +}
|
| +
|
| +void VideoCaptureDeviceAndroid::SetErrorState(
|
| + const tracked_objects::Location& from_here,
|
| + const std::string& reason) {
|
| + {
|
| + base::AutoLock lock(lock_);
|
| + state_ = kError;
|
| + if (!client_)
|
| + return;
|
| + client_->OnError(from_here, reason);
|
| + }
|
| +}
|
| +
|
| } // namespace media
|
|
|