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/video/capture/android/video_capture_device_android.h" 6 7 #include <string> 8 9 #include "base/android/jni_android.h" 10 #include "base/android/jni_string.h" 11 #include "base/android/scoped_java_ref.h" 12 #include "base/strings/string_number_conversions.h" 13 #include "base/strings/stringprintf.h" 14 #include "jni/VideoCapture_jni.h" 15 #include "media/base/video_util.h" 16 17 using base::android::AttachCurrentThread; 18 using base::android::CheckException; 19 using base::android::GetClass; 20 using base::android::MethodID; 21 using base::android::JavaRef; 22 using base::android::ScopedJavaLocalRef; 23 24 namespace media { 25 26 // static 27 void VideoCaptureDevice::GetDeviceNames(Names* device_names) { 28 device_names->clear(); 29 30 JNIEnv* env = AttachCurrentThread(); 31 32 int num_cameras = Java_ChromiumCameraInfo_getNumberOfCameras(env); 33 DVLOG(1) << "VideoCaptureDevice::GetDeviceNames: num_cameras=" << num_cameras; 34 if (num_cameras <= 0) 35 return; 36 37 for (int camera_id = num_cameras - 1; camera_id >= 0; --camera_id) { 38 ScopedJavaLocalRef<jobject> ci = 39 Java_ChromiumCameraInfo_getAt(env, camera_id); 40 41 Name name( 42 base::android::ConvertJavaStringToUTF8( 43 Java_ChromiumCameraInfo_getDeviceName(env, ci.obj())), 44 base::StringPrintf("%d", Java_ChromiumCameraInfo_getId(env, ci.obj()))); 45 device_names->push_back(name); 46 47 DVLOG(1) << "VideoCaptureDevice::GetDeviceNames: camera device_name=" 48 << name.name() 49 << ", unique_id=" 50 << name.id() 51 << ", orientation " 52 << Java_ChromiumCameraInfo_getOrientation(env, ci.obj()); 53 } 54 } 55 56 // static 57 void VideoCaptureDevice::GetDeviceSupportedFormats(const Name& device, 58 VideoCaptureFormats* formats) { 59 NOTIMPLEMENTED(); 60 } 61 62 const std::string VideoCaptureDevice::Name::GetModel() const { 63 // Android cameras are not typically USB devices, and this method is currently 64 // only used for USB model identifiers, so this implementation just indicates 65 // an unknown device model. 66 return ""; 67 } 68 69 // static 70 VideoCaptureDevice* VideoCaptureDevice::Create(const Name& device_name) { 71 return VideoCaptureDeviceAndroid::Create(device_name); 72 } 73 74 // static 75 VideoCaptureDevice* VideoCaptureDeviceAndroid::Create(const Name& device_name) { 76 scoped_ptr<VideoCaptureDeviceAndroid> ret( 77 new VideoCaptureDeviceAndroid(device_name)); 78 if (ret->Init()) 79 return ret.release(); 80 return NULL; 81 } 82 83 // static 84 bool VideoCaptureDeviceAndroid::RegisterVideoCaptureDevice(JNIEnv* env) { 85 return RegisterNativesImpl(env); 86 } 87 88 VideoCaptureDeviceAndroid::VideoCaptureDeviceAndroid(const Name& device_name) 89 : state_(kIdle), got_first_frame_(false), device_name_(device_name) {} 90 91 VideoCaptureDeviceAndroid::~VideoCaptureDeviceAndroid() { 92 StopAndDeAllocate(); 93 } 94 95 bool VideoCaptureDeviceAndroid::Init() { 96 int id; 97 if (!base::StringToInt(device_name_.id(), &id)) 98 return false; 99 100 JNIEnv* env = AttachCurrentThread(); 101 102 j_capture_.Reset(Java_VideoCapture_createVideoCapture( 103 env, base::android::GetApplicationContext(), id, 104 reinterpret_cast<intptr_t>(this))); 105 106 return true; 107 } 108 109 void VideoCaptureDeviceAndroid::AllocateAndStart( 110 const VideoCaptureParams& params, 111 scoped_ptr<Client> client) { 112 DVLOG(1) << "VideoCaptureDeviceAndroid::AllocateAndStart"; 113 { 114 base::AutoLock lock(lock_); 115 if (state_ != kIdle) 116 return; 117 client_ = client.Pass(); 118 got_first_frame_ = false; 119 } 120 121 JNIEnv* env = AttachCurrentThread(); 122 123 jboolean ret = 124 Java_VideoCapture_allocate(env, 125 j_capture_.obj(), 126 params.requested_format.frame_size.width(), 127 params.requested_format.frame_size.height(), 128 params.requested_format.frame_rate); 129 if (!ret) { 130 SetErrorState("failed to allocate"); 131 return; 132 } 133 134 // Store current width and height. 135 capture_format_.frame_size.SetSize( 136 Java_VideoCapture_queryWidth(env, j_capture_.obj()), 137 Java_VideoCapture_queryHeight(env, j_capture_.obj())); 138 capture_format_.frame_rate = 139 Java_VideoCapture_queryFrameRate(env, j_capture_.obj()); 140 capture_format_.pixel_format = GetColorspace(); 141 DCHECK_NE(capture_format_.pixel_format, media::PIXEL_FORMAT_UNKNOWN); 142 CHECK(capture_format_.frame_size.GetArea() > 0); 143 CHECK(!(capture_format_.frame_size.width() % 2)); 144 CHECK(!(capture_format_.frame_size.height() % 2)); 145 146 if (capture_format_.frame_rate > 0) { 147 frame_interval_ = base::TimeDelta::FromMicroseconds( 148 (base::Time::kMicrosecondsPerSecond + capture_format_.frame_rate - 1) / 149 capture_format_.frame_rate); 150 } 151 152 DVLOG(1) << "VideoCaptureDeviceAndroid::Allocate: queried frame_size=" 153 << capture_format_.frame_size.ToString() 154 << ", frame_rate=" << capture_format_.frame_rate; 155 156 jint result = Java_VideoCapture_startCapture(env, j_capture_.obj()); 157 if (result < 0) { 158 SetErrorState("failed to start capture"); 159 return; 160 } 161 162 { 163 base::AutoLock lock(lock_); 164 state_ = kCapturing; 165 } 166 } 167 168 void VideoCaptureDeviceAndroid::StopAndDeAllocate() { 169 DVLOG(1) << "VideoCaptureDeviceAndroid::StopAndDeAllocate"; 170 { 171 base::AutoLock lock(lock_); 172 if (state_ != kCapturing && state_ != kError) 173 return; 174 } 175 176 JNIEnv* env = AttachCurrentThread(); 177 178 jint ret = Java_VideoCapture_stopCapture(env, j_capture_.obj()); 179 if (ret < 0) { 180 SetErrorState("failed to stop capture"); 181 return; 182 } 183 184 { 185 base::AutoLock lock(lock_); 186 state_ = kIdle; 187 client_.reset(); 188 } 189 190 Java_VideoCapture_deallocate(env, j_capture_.obj()); 191 } 192 193 void VideoCaptureDeviceAndroid::OnFrameAvailable( 194 JNIEnv* env, 195 jobject obj, 196 jbyteArray data, 197 jint length, 198 jint rotation) { 199 DVLOG(3) << "VideoCaptureDeviceAndroid::OnFrameAvailable: length =" << length; 200 201 base::AutoLock lock(lock_); 202 if (state_ != kCapturing || !client_.get()) 203 return; 204 205 jbyte* buffer = env->GetByteArrayElements(data, NULL); 206 if (!buffer) { 207 LOG(ERROR) << "VideoCaptureDeviceAndroid::OnFrameAvailable: " 208 "failed to GetByteArrayElements"; 209 return; 210 } 211 212 base::TimeTicks current_time = base::TimeTicks::Now(); 213 if (!got_first_frame_) { 214 // Set aside one frame allowance for fluctuation. 215 expected_next_frame_time_ = current_time - frame_interval_; 216 got_first_frame_ = true; 217 } 218 219 // Deliver the frame when it doesn't arrive too early. 220 if (expected_next_frame_time_ <= current_time) { 221 expected_next_frame_time_ += frame_interval_; 222 223 client_->OnIncomingCapturedFrame(reinterpret_cast<uint8*>(buffer), 224 length, 225 base::Time::Now(), 226 rotation, 227 capture_format_); 228 } 229 230 env->ReleaseByteArrayElements(data, buffer, JNI_ABORT); 231 } 232 233 VideoPixelFormat VideoCaptureDeviceAndroid::GetColorspace() { 234 JNIEnv* env = AttachCurrentThread(); 235 int current_capture_colorspace = 236 Java_VideoCapture_getColorspace(env, j_capture_.obj()); 237 switch (current_capture_colorspace){ 238 case ANDROID_IMAGEFORMAT_YV12: 239 return media::PIXEL_FORMAT_YV12; 240 case ANDROID_IMAGEFORMAT_NV21: 241 return media::PIXEL_FORMAT_NV21; 242 case ANDROID_IMAGEFORMAT_YUY2: 243 return media::PIXEL_FORMAT_YUY2; 244 case ANDROID_IMAGEFORMAT_NV16: 245 case ANDROID_IMAGEFORMAT_JPEG: 246 case ANDROID_IMAGEFORMAT_RGB_565: 247 case ANDROID_IMAGEFORMAT_UNKNOWN: 248 // NOTE(mcasas): NV16, JPEG, RGB565 not supported in VideoPixelFormat. 249 default: 250 return media::PIXEL_FORMAT_UNKNOWN; 251 } 252 } 253 254 void VideoCaptureDeviceAndroid::SetErrorState(const std::string& reason) { 255 LOG(ERROR) << "VideoCaptureDeviceAndroid::SetErrorState: " << reason; 256 { 257 base::AutoLock lock(lock_); 258 state_ = kError; 259 } 260 client_->OnError(); 261 } 262 263 } // namespace media 264