blob: 6fba8d35047c4324176f5fddfe9f377dafe3cf3b [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/scoped_java_ref.h"
#include "base/strings/string_number_conversions.h"
#include "jni/VideoCapture_jni.h"
#include "media/video/capture/android/video_capture_device_factory_android.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
bool VideoCaptureDeviceAndroid::RegisterVideoCaptureDevice(JNIEnv* env) {
return RegisterNativesImpl(env);
}
// TODO(mcasas): Remove the following static methods when they are no longer
// referenced from VideoCaptureDeviceFactory, i.e. when all OS platforms have
// splitted the VideoCaptureDevice into VideoCaptureDevice and
// VideoCaptureDeviceFactory.
// static
VideoCaptureDevice* VideoCaptureDevice::Create(
scoped_refptr<base::SingleThreadTaskRunner> ui_task_runner,
const Name& device_name) {
NOTREACHED();
return NULL;
}
// static
void VideoCaptureDevice::GetDeviceNames(Names* device_names) {
NOTREACHED();
}
// static
void VideoCaptureDevice::GetDeviceSupportedFormats(
const Name& device,
VideoCaptureFormats* supported_formats) {
NOTREACHED();
}
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 "";
}
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;
j_capture_.Reset(VideoCaptureDeviceFactoryAndroid::createVideoCaptureAndroid(
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_->OnIncomingCapturedData(reinterpret_cast<uint8*>(buffer),
length,
capture_format_,
rotation,
base::TimeTicks::Now());
}
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_UNKNOWN:
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(reason);
}
} // namespace media