| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2013 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "media/capture/video/android/video_capture_device_android.h" | |
| 6 | |
| 7 #include <stdint.h> | |
| 8 #include <utility> | |
| 9 | |
| 10 #include "base/android/jni_android.h" | |
| 11 #include "base/android/jni_array.h" | |
| 12 #include "base/android/jni_string.h" | |
| 13 #include "base/strings/string_number_conversions.h" | |
| 14 #include "jni/VideoCapture_jni.h" | |
| 15 #include "media/capture/video/android/photo_capabilities.h" | |
| 16 #include "media/capture/video/android/video_capture_device_factory_android.h" | |
| 17 #include "mojo/public/cpp/bindings/string.h" | |
| 18 | |
| 19 using base::android::AttachCurrentThread; | |
| 20 using base::android::CheckException; | |
| 21 using base::android::GetClass; | |
| 22 using base::android::MethodID; | |
| 23 using base::android::JavaRef; | |
| 24 using base::android::ScopedJavaLocalRef; | |
| 25 | |
| 26 namespace media { | |
| 27 | |
| 28 // static | |
| 29 bool VideoCaptureDeviceAndroid::RegisterVideoCaptureDevice(JNIEnv* env) { | |
| 30 return RegisterNativesImpl(env); | |
| 31 } | |
| 32 | |
| 33 const std::string VideoCaptureDevice::Name::GetModel() const { | |
| 34 // Android cameras are not typically USB devices, and this method is currently | |
| 35 // only used for USB model identifiers, so this implementation just indicates | |
| 36 // an unknown device model. | |
| 37 return ""; | |
| 38 } | |
| 39 | |
| 40 VideoCaptureDeviceAndroid::VideoCaptureDeviceAndroid(const Name& device_name) | |
| 41 : state_(kIdle), got_first_frame_(false), device_name_(device_name) { | |
| 42 } | |
| 43 | |
| 44 VideoCaptureDeviceAndroid::~VideoCaptureDeviceAndroid() { | |
| 45 StopAndDeAllocate(); | |
| 46 } | |
| 47 | |
| 48 bool VideoCaptureDeviceAndroid::Init() { | |
| 49 int id; | |
| 50 if (!base::StringToInt(device_name_.id(), &id)) | |
| 51 return false; | |
| 52 | |
| 53 j_capture_.Reset(VideoCaptureDeviceFactoryAndroid::createVideoCaptureAndroid( | |
| 54 id, reinterpret_cast<intptr_t>(this))); | |
| 55 return true; | |
| 56 } | |
| 57 | |
| 58 void VideoCaptureDeviceAndroid::AllocateAndStart( | |
| 59 const VideoCaptureParams& params, | |
| 60 std::unique_ptr<Client> client) { | |
| 61 DVLOG(1) << __FUNCTION__; | |
| 62 { | |
| 63 base::AutoLock lock(lock_); | |
| 64 if (state_ != kIdle) | |
| 65 return; | |
| 66 client_ = std::move(client); | |
| 67 got_first_frame_ = false; | |
| 68 } | |
| 69 | |
| 70 JNIEnv* env = AttachCurrentThread(); | |
| 71 | |
| 72 jboolean ret = Java_VideoCapture_allocate( | |
| 73 env, j_capture_.obj(), params.requested_format.frame_size.width(), | |
| 74 params.requested_format.frame_size.height(), | |
| 75 params.requested_format.frame_rate); | |
| 76 if (!ret) { | |
| 77 SetErrorState(FROM_HERE, "failed to allocate"); | |
| 78 return; | |
| 79 } | |
| 80 | |
| 81 // Store current width and height. | |
| 82 capture_format_.frame_size.SetSize( | |
| 83 Java_VideoCapture_queryWidth(env, j_capture_.obj()), | |
| 84 Java_VideoCapture_queryHeight(env, j_capture_.obj())); | |
| 85 capture_format_.frame_rate = | |
| 86 Java_VideoCapture_queryFrameRate(env, j_capture_.obj()); | |
| 87 capture_format_.pixel_format = GetColorspace(); | |
| 88 DCHECK_NE(capture_format_.pixel_format, media::PIXEL_FORMAT_UNKNOWN); | |
| 89 CHECK(capture_format_.frame_size.GetArea() > 0); | |
| 90 CHECK(!(capture_format_.frame_size.width() % 2)); | |
| 91 CHECK(!(capture_format_.frame_size.height() % 2)); | |
| 92 | |
| 93 if (capture_format_.frame_rate > 0) { | |
| 94 frame_interval_ = base::TimeDelta::FromMicroseconds( | |
| 95 (base::Time::kMicrosecondsPerSecond + capture_format_.frame_rate - 1) / | |
| 96 capture_format_.frame_rate); | |
| 97 } | |
| 98 | |
| 99 DVLOG(1) << "VideoCaptureDeviceAndroid::Allocate: queried frame_size=" | |
| 100 << capture_format_.frame_size.ToString() | |
| 101 << ", frame_rate=" << capture_format_.frame_rate; | |
| 102 | |
| 103 ret = Java_VideoCapture_startCapture(env, j_capture_.obj()); | |
| 104 if (!ret) { | |
| 105 SetErrorState(FROM_HERE, "failed to start capture"); | |
| 106 return; | |
| 107 } | |
| 108 | |
| 109 { | |
| 110 base::AutoLock lock(lock_); | |
| 111 state_ = kCapturing; | |
| 112 } | |
| 113 } | |
| 114 | |
| 115 void VideoCaptureDeviceAndroid::StopAndDeAllocate() { | |
| 116 DVLOG(1) << __FUNCTION__; | |
| 117 { | |
| 118 base::AutoLock lock(lock_); | |
| 119 if (state_ != kCapturing && state_ != kError) | |
| 120 return; | |
| 121 } | |
| 122 | |
| 123 JNIEnv* env = AttachCurrentThread(); | |
| 124 | |
| 125 jboolean ret = Java_VideoCapture_stopCapture(env, j_capture_.obj()); | |
| 126 if (!ret) { | |
| 127 SetErrorState(FROM_HERE, "failed to stop capture"); | |
| 128 return; | |
| 129 } | |
| 130 | |
| 131 { | |
| 132 base::AutoLock lock(lock_); | |
| 133 state_ = kIdle; | |
| 134 client_.reset(); | |
| 135 } | |
| 136 | |
| 137 Java_VideoCapture_deallocate(env, j_capture_.obj()); | |
| 138 } | |
| 139 | |
| 140 void VideoCaptureDeviceAndroid::TakePhoto(TakePhotoCallback callback) { | |
| 141 DVLOG(1) << __FUNCTION__; | |
| 142 { | |
| 143 base::AutoLock lock(lock_); | |
| 144 if (state_ != kCapturing) | |
| 145 return; | |
| 146 } | |
| 147 | |
| 148 JNIEnv* env = AttachCurrentThread(); | |
| 149 | |
| 150 // Make copy on the heap so we can pass the pointer through JNI. | |
| 151 std::unique_ptr<TakePhotoCallback> heap_callback( | |
| 152 new TakePhotoCallback(std::move(callback))); | |
| 153 const intptr_t callback_id = reinterpret_cast<intptr_t>(heap_callback.get()); | |
| 154 if (!Java_VideoCapture_takePhoto(env, j_capture_.obj(), callback_id)) | |
| 155 return; | |
| 156 | |
| 157 { | |
| 158 base::AutoLock lock(photo_callbacks_lock_); | |
| 159 photo_callbacks_.push_back(std::move(heap_callback)); | |
| 160 } | |
| 161 } | |
| 162 | |
| 163 void VideoCaptureDeviceAndroid::GetPhotoCapabilities( | |
| 164 GetPhotoCapabilitiesCallback callback) { | |
| 165 JNIEnv* env = AttachCurrentThread(); | |
| 166 | |
| 167 PhotoCapabilities caps( | |
| 168 Java_VideoCapture_getPhotoCapabilities(env, j_capture_.obj())); | |
| 169 | |
| 170 // TODO(mcasas): Manual member copying sucks, consider adding typemapping from | |
| 171 // PhotoCapabilities to mojom::PhotoCapabilitiesPtr, https://crbug.com/622002. | |
| 172 mojom::PhotoCapabilitiesPtr photo_capabilities = | |
| 173 mojom::PhotoCapabilities::New(); | |
| 174 photo_capabilities->zoom = mojom::Range::New(); | |
| 175 photo_capabilities->zoom->current = caps.getCurrentZoom(); | |
| 176 photo_capabilities->zoom->max = caps.getMaxZoom(); | |
| 177 photo_capabilities->zoom->min = caps.getMinZoom(); | |
| 178 callback.Run(std::move(photo_capabilities)); | |
| 179 } | |
| 180 | |
| 181 void VideoCaptureDeviceAndroid::SetPhotoOptions( | |
| 182 mojom::PhotoSettingsPtr settings, | |
| 183 SetPhotoOptionsCallback callback) { | |
| 184 JNIEnv* env = AttachCurrentThread(); | |
| 185 if (settings->has_zoom) | |
| 186 Java_VideoCapture_setZoom(env, j_capture_.obj(), settings->zoom); | |
| 187 callback.Run(true); | |
| 188 } | |
| 189 | |
| 190 void VideoCaptureDeviceAndroid::OnFrameAvailable( | |
| 191 JNIEnv* env, | |
| 192 const JavaParamRef<jobject>& obj, | |
| 193 const JavaParamRef<jbyteArray>& data, | |
| 194 jint length, | |
| 195 jint rotation) { | |
| 196 DVLOG(3) << __FUNCTION__ << " length =" << length; | |
| 197 | |
| 198 base::AutoLock lock(lock_); | |
| 199 if (state_ != kCapturing || !client_.get()) | |
| 200 return; | |
| 201 | |
| 202 jbyte* buffer = env->GetByteArrayElements(data, NULL); | |
| 203 if (!buffer) { | |
| 204 LOG(ERROR) << "VideoCaptureDeviceAndroid::OnFrameAvailable: " | |
| 205 "failed to GetByteArrayElements"; | |
| 206 return; | |
| 207 } | |
| 208 | |
| 209 base::TimeTicks current_time = base::TimeTicks::Now(); | |
| 210 if (!got_first_frame_) { | |
| 211 // Set aside one frame allowance for fluctuation. | |
| 212 expected_next_frame_time_ = current_time - frame_interval_; | |
| 213 first_ref_time_ = current_time; | |
| 214 got_first_frame_ = true; | |
| 215 } | |
| 216 | |
| 217 // Deliver the frame when it doesn't arrive too early. | |
| 218 if (expected_next_frame_time_ <= current_time) { | |
| 219 expected_next_frame_time_ += frame_interval_; | |
| 220 | |
| 221 // TODO(qiangchen): Investigate how to get raw timestamp for Android, | |
| 222 // rather than using reference time to calculate timestamp. | |
| 223 client_->OnIncomingCapturedData(reinterpret_cast<uint8_t*>(buffer), length, | |
| 224 capture_format_, rotation, current_time, | |
| 225 current_time - first_ref_time_); | |
| 226 } | |
| 227 | |
| 228 env->ReleaseByteArrayElements(data, buffer, JNI_ABORT); | |
| 229 } | |
| 230 | |
| 231 void VideoCaptureDeviceAndroid::OnError(JNIEnv* env, | |
| 232 const JavaParamRef<jobject>& obj, | |
| 233 const JavaParamRef<jstring>& message) { | |
| 234 SetErrorState(FROM_HERE, | |
| 235 base::android::ConvertJavaStringToUTF8(env, message)); | |
| 236 } | |
| 237 | |
| 238 void VideoCaptureDeviceAndroid::OnPhotoTaken( | |
| 239 JNIEnv* env, | |
| 240 const base::android::JavaParamRef<jobject>& obj, | |
| 241 jlong callback_id, | |
| 242 const base::android::JavaParamRef<jbyteArray>& data) { | |
| 243 DCHECK(callback_id); | |
| 244 | |
| 245 base::AutoLock lock(photo_callbacks_lock_); | |
| 246 | |
| 247 TakePhotoCallback* const cb = | |
| 248 reinterpret_cast<TakePhotoCallback*>(callback_id); | |
| 249 // Search for the pointer |cb| in the list of |photo_callbacks_|. | |
| 250 const auto reference_it = | |
| 251 std::find_if(photo_callbacks_.begin(), photo_callbacks_.end(), | |
| 252 [cb](const std::unique_ptr<TakePhotoCallback>& callback) { | |
| 253 return callback.get() == cb; | |
| 254 }); | |
| 255 if (reference_it == photo_callbacks_.end()) { | |
| 256 NOTREACHED() << "|callback_id| not found."; | |
| 257 return; | |
| 258 } | |
| 259 | |
| 260 std::vector<uint8_t> native_data; | |
| 261 base::android::JavaByteArrayToByteVector(env, data.obj(), &native_data); | |
| 262 | |
| 263 cb->Run(mojo::String::From(native_data.empty() ? "" : "image/jpeg"), | |
| 264 mojo::Array<uint8_t>::From(native_data)); | |
| 265 | |
| 266 photo_callbacks_.erase(reference_it); | |
| 267 } | |
| 268 | |
| 269 VideoPixelFormat VideoCaptureDeviceAndroid::GetColorspace() { | |
| 270 JNIEnv* env = AttachCurrentThread(); | |
| 271 const int current_capture_colorspace = | |
| 272 Java_VideoCapture_getColorspace(env, j_capture_.obj()); | |
| 273 switch (current_capture_colorspace) { | |
| 274 case ANDROID_IMAGE_FORMAT_YV12: | |
| 275 return media::PIXEL_FORMAT_YV12; | |
| 276 case ANDROID_IMAGE_FORMAT_YUV_420_888: | |
| 277 return media::PIXEL_FORMAT_I420; | |
| 278 case ANDROID_IMAGE_FORMAT_NV21: | |
| 279 return media::PIXEL_FORMAT_NV21; | |
| 280 case ANDROID_IMAGE_FORMAT_UNKNOWN: | |
| 281 default: | |
| 282 return media::PIXEL_FORMAT_UNKNOWN; | |
| 283 } | |
| 284 } | |
| 285 | |
| 286 void VideoCaptureDeviceAndroid::SetErrorState( | |
| 287 const tracked_objects::Location& from_here, | |
| 288 const std::string& reason) { | |
| 289 { | |
| 290 base::AutoLock lock(lock_); | |
| 291 state_ = kError; | |
| 292 } | |
| 293 client_->OnError(from_here, reason); | |
| 294 } | |
| 295 | |
| 296 } // namespace media | |
| OLD | NEW |