blob: adfa9a3455c12bf5293b8fd1aae87a5a965a472c [file] [log] [blame]
// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "media/video/capture/android/video_capture_device_android.h"
#include <string>
#include "base/android/jni_android.h"
#include "base/android/jni_string.h"
#include "base/android/scoped_java_ref.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/stringprintf.h"
#include "jni/VideoCapture_jni.h"
#include "media/base/video_util.h"
using base::android::AttachCurrentThread;
using base::android::CheckException;
using base::android::GetClass;
using base::android::MethodID;
using base::android::JavaRef;
using base::android::ScopedJavaLocalRef;
namespace media {
// static
void VideoCaptureDevice::GetDeviceNames(Names* device_names) {
device_names->clear();
JNIEnv* env = AttachCurrentThread();
int num_cameras = Java_ChromiumCameraInfo_getNumberOfCameras(env);
DVLOG(1) << "VideoCaptureDevice::GetDeviceNames: num_cameras=" << num_cameras;
if (num_cameras <= 0)
return;
for (int camera_id = num_cameras - 1; camera_id >= 0; --camera_id) {
ScopedJavaLocalRef<jobject> ci =
Java_ChromiumCameraInfo_getAt(env, camera_id);
Name name(
base::android::ConvertJavaStringToUTF8(
Java_ChromiumCameraInfo_getDeviceName(env, ci.obj())),
base::StringPrintf("%d", Java_ChromiumCameraInfo_getId(env, ci.obj())));
device_names->push_back(name);
DVLOG(1) << "VideoCaptureDevice::GetDeviceNames: camera device_name="
<< name.name()
<< ", unique_id="
<< name.id()
<< ", orientation "
<< Java_ChromiumCameraInfo_getOrientation(env, ci.obj());
}
}
// static
void VideoCaptureDevice::GetDeviceSupportedFormats(const Name& device,
VideoCaptureFormats* formats) {
NOTIMPLEMENTED();
}
const std::string VideoCaptureDevice::Name::GetModel() const {
// Android cameras are not typically USB devices, and this method is currently
// only used for USB model identifiers, so this implementation just indicates
// an unknown device model.
return "";
}
// static
VideoCaptureDevice* VideoCaptureDevice::Create(const Name& device_name) {
return VideoCaptureDeviceAndroid::Create(device_name);
}
// static
VideoCaptureDevice* VideoCaptureDeviceAndroid::Create(const Name& device_name) {
scoped_ptr<VideoCaptureDeviceAndroid> ret(
new VideoCaptureDeviceAndroid(device_name));
if (ret->Init())
return ret.release();
return NULL;
}
// static
bool VideoCaptureDeviceAndroid::RegisterVideoCaptureDevice(JNIEnv* env) {
return RegisterNativesImpl(env);
}
VideoCaptureDeviceAndroid::VideoCaptureDeviceAndroid(const Name& device_name)
: state_(kIdle), got_first_frame_(false), device_name_(device_name) {}
VideoCaptureDeviceAndroid::~VideoCaptureDeviceAndroid() {
StopAndDeAllocate();
}
bool VideoCaptureDeviceAndroid::Init() {
int id;
if (!base::StringToInt(device_name_.id(), &id))
return false;
JNIEnv* env = AttachCurrentThread();
j_capture_.Reset(Java_VideoCapture_createVideoCapture(
env, base::android::GetApplicationContext(), id,
reinterpret_cast<intptr_t>(this)));
return true;
}
void VideoCaptureDeviceAndroid::AllocateAndStart(
const VideoCaptureParams& params,
scoped_ptr<Client> client) {
DVLOG(1) << "VideoCaptureDeviceAndroid::AllocateAndStart";
{
base::AutoLock lock(lock_);
if (state_ != kIdle)
return;
client_ = client.Pass();
got_first_frame_ = false;
}
JNIEnv* env = AttachCurrentThread();
jboolean ret =
Java_VideoCapture_allocate(env,
j_capture_.obj(),
params.requested_format.frame_size.width(),
params.requested_format.frame_size.height(),
params.requested_format.frame_rate);
if (!ret) {
SetErrorState("failed to allocate");
return;
}
// Store current width and height.
capture_format_.frame_size.SetSize(
Java_VideoCapture_queryWidth(env, j_capture_.obj()),
Java_VideoCapture_queryHeight(env, j_capture_.obj()));
capture_format_.frame_rate =
Java_VideoCapture_queryFrameRate(env, j_capture_.obj());
capture_format_.pixel_format = GetColorspace();
DCHECK_NE(capture_format_.pixel_format, media::PIXEL_FORMAT_UNKNOWN);
CHECK(capture_format_.frame_size.GetArea() > 0);
CHECK(!(capture_format_.frame_size.width() % 2));
CHECK(!(capture_format_.frame_size.height() % 2));
if (capture_format_.frame_rate > 0) {
frame_interval_ = base::TimeDelta::FromMicroseconds(
(base::Time::kMicrosecondsPerSecond + capture_format_.frame_rate - 1) /
capture_format_.frame_rate);
}
DVLOG(1) << "VideoCaptureDeviceAndroid::Allocate: queried frame_size="
<< capture_format_.frame_size.ToString()
<< ", frame_rate=" << capture_format_.frame_rate;
jint result = Java_VideoCapture_startCapture(env, j_capture_.obj());
if (result < 0) {
SetErrorState("failed to start capture");
return;
}
{
base::AutoLock lock(lock_);
state_ = kCapturing;
}
}
void VideoCaptureDeviceAndroid::StopAndDeAllocate() {
DVLOG(1) << "VideoCaptureDeviceAndroid::StopAndDeAllocate";
{
base::AutoLock lock(lock_);
if (state_ != kCapturing && state_ != kError)
return;
}
JNIEnv* env = AttachCurrentThread();
jint ret = Java_VideoCapture_stopCapture(env, j_capture_.obj());
if (ret < 0) {
SetErrorState("failed to stop capture");
return;
}
{
base::AutoLock lock(lock_);
state_ = kIdle;
client_.reset();
}
Java_VideoCapture_deallocate(env, j_capture_.obj());
}
void VideoCaptureDeviceAndroid::OnFrameAvailable(
JNIEnv* env,
jobject obj,
jbyteArray data,
jint length,
jint rotation) {
DVLOG(3) << "VideoCaptureDeviceAndroid::OnFrameAvailable: length =" << length;
base::AutoLock lock(lock_);
if (state_ != kCapturing || !client_.get())
return;
jbyte* buffer = env->GetByteArrayElements(data, NULL);
if (!buffer) {
LOG(ERROR) << "VideoCaptureDeviceAndroid::OnFrameAvailable: "
"failed to GetByteArrayElements";
return;
}
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_;
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_;
client_->OnIncomingCapturedFrame(reinterpret_cast<uint8*>(buffer),
length,
base::Time::Now(),
rotation,
capture_format_);
}
env->ReleaseByteArrayElements(data, buffer, JNI_ABORT);
}
VideoPixelFormat VideoCaptureDeviceAndroid::GetColorspace() {
JNIEnv* env = AttachCurrentThread();
int current_capture_colorspace =
Java_VideoCapture_getColorspace(env, j_capture_.obj());
switch (current_capture_colorspace){
case ANDROID_IMAGEFORMAT_YV12:
return media::PIXEL_FORMAT_YV12;
case ANDROID_IMAGEFORMAT_NV21:
return media::PIXEL_FORMAT_NV21;
case ANDROID_IMAGEFORMAT_YUY2:
return media::PIXEL_FORMAT_YUY2;
case ANDROID_IMAGEFORMAT_NV16:
case ANDROID_IMAGEFORMAT_JPEG:
case ANDROID_IMAGEFORMAT_RGB_565:
case ANDROID_IMAGEFORMAT_UNKNOWN:
// NOTE(mcasas): NV16, JPEG, RGB565 not supported in VideoPixelFormat.
default:
return media::PIXEL_FORMAT_UNKNOWN;
}
}
void VideoCaptureDeviceAndroid::SetErrorState(const std::string& reason) {
LOG(ERROR) << "VideoCaptureDeviceAndroid::SetErrorState: " << reason;
{
base::AutoLock lock(lock_);
state_ = kError;
}
client_->OnError();
}
} // namespace media