Chromium Code Reviews| Index: media/capture/content/android/screen_capture_machine_android.cc |
| diff --git a/media/capture/content/android/screen_capture_machine_android.cc b/media/capture/content/android/screen_capture_machine_android.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..ab07023017e33fc7a463149833035bb2898b66ad |
| --- /dev/null |
| +++ b/media/capture/content/android/screen_capture_machine_android.cc |
| @@ -0,0 +1,212 @@ |
| +// Copyright 2016 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/capture/content/android/screen_capture_machine_android.h" |
| + |
| +#include "base/android/context_utils.h" |
| +#include "base/android/jni_android.h" |
| +#include "base/android/scoped_java_ref.h" |
| +#include "jni/ScreenCapture_jni.h" |
| +#include "media/base/video_capture_types.h" |
| +#include "media/base/yuv_convert.h" |
| +#include "media/capture/content/video_capture_oracle.h" |
| +#include "third_party/libyuv/include/libyuv.h" |
| + |
| +using base::android::AttachCurrentThread; |
| + |
| +namespace media { |
| + |
| +// static |
| +bool ScreenCaptureMachineAndroid::RegisterScreenCaptureMachine(JNIEnv* env) { |
| + return RegisterNativesImpl(env); |
| +} |
| + |
| +ScreenCaptureMachineAndroid::ScreenCaptureMachineAndroid() {} |
| + |
| +ScreenCaptureMachineAndroid::~ScreenCaptureMachineAndroid() {} |
| + |
| +// static |
| +ScopedJavaLocalRef<jobject> |
| +ScreenCaptureMachineAndroid::createScreenCaptureMachineAndroid( |
| + jlong nativeScreenCaptureMachineAndroid) { |
| + return (Java_ScreenCapture_createScreenCaptureMachine( |
| + AttachCurrentThread(), base::android::GetApplicationContext(), |
| + nativeScreenCaptureMachineAndroid)); |
| +} |
| + |
| +void ScreenCaptureMachineAndroid::OnRGBAFrameAvailable(JNIEnv* env, |
| + jobject obj, |
| + jobject buf, |
| + jint width, |
| + jint height, |
| + jint row_stride, |
| + jlong timestamp) { |
| + scoped_refptr<VideoFrame> i420_frame = VideoFrame::CreateFrame( |
| + PIXEL_FORMAT_I420, gfx::Size(width, height), gfx::Rect(width, height), |
| + gfx::Size(width, height), base::TimeDelta()); |
| + |
| + // ABGR little endian (rgba in memory) to I420. |
| + libyuv::ABGRToI420( |
| + reinterpret_cast<uint8_t*>(env->GetDirectBufferAddress(buf)), row_stride, |
| + i420_frame->visible_data(VideoFrame::kYPlane), |
| + i420_frame->stride(VideoFrame::kYPlane), |
| + i420_frame->visible_data(VideoFrame::kUPlane), |
| + i420_frame->stride(VideoFrame::kUPlane), |
| + i420_frame->visible_data(VideoFrame::kVPlane), |
| + i420_frame->stride(VideoFrame::kVPlane), |
| + i420_frame->visible_rect().width(), i420_frame->visible_rect().height()); |
| + |
| + OnIncomingFrameAvailable(i420_frame->visible_data(VideoFrame::kYPlane), |
| + i420_frame->stride(VideoFrame::kYPlane), |
| + i420_frame->visible_data(VideoFrame::kUPlane), |
| + i420_frame->stride(VideoFrame::kUPlane), |
| + i420_frame->visible_data(VideoFrame::kVPlane), |
| + i420_frame->stride(VideoFrame::kVPlane), |
| + i420_frame->visible_rect().width(), |
| + i420_frame->visible_rect().height(), |
| + static_cast<int64_t>(timestamp)); |
| +} |
| + |
| +void ScreenCaptureMachineAndroid::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, |
| + jlong timestamp) { |
| + 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); |
| + |
| + int uv_stride = uv_row_stride; |
| + if (uv_pixel_stride != 1) { |
| + // U and V planes have |pixelStride| = 2, unpack them here. |
| + int uv_plane_len = (int)env->GetDirectBufferCapacity(u_buffer); |
|
miu
2016/06/02 22:33:51
Please add: CHECK_EQ(uv_pixel_stride, 2); (just i
braveyao
2016/06/08 20:39:33
Done.
|
| + for (int index = 0; index * uv_pixel_stride <= uv_plane_len; index++) { |
| + u_src[index] = u_src[index * uv_pixel_stride]; |
| + v_src[index] = v_src[index * uv_pixel_stride]; |
| + } |
| + uv_stride /= uv_pixel_stride; |
| + } |
| + |
| + OnIncomingFrameAvailable(y_src, y_stride, u_src, uv_stride, v_src, uv_stride, |
| + width, height, static_cast<int64_t>(timestamp)); |
| +} |
| + |
| +void ScreenCaptureMachineAndroid::OnActivityResult(JNIEnv* env, |
| + jobject obj, |
| + jboolean result) { |
| + if (!result) { |
| + oracle_proxy_->ReportError(FROM_HERE, "The user denied screen capture"); |
| + return; |
| + } |
| + |
| + Java_ScreenCapture_startCapture(env, obj); |
| +} |
| + |
| +void ScreenCaptureMachineAndroid::Start( |
| + const scoped_refptr<ThreadSafeCaptureOracle>& oracle_proxy, |
| + const VideoCaptureParams& params, |
| + const base::Callback<void(bool)> callback) { |
| + DCHECK(oracle_proxy.get()); |
| + oracle_proxy_ = oracle_proxy; |
| + |
| + j_capture_.Reset( |
| + createScreenCaptureMachineAndroid(reinterpret_cast<intptr_t>(this))); |
| + |
| + if (j_capture_.obj() == nullptr) { |
| + LOG(ERROR) << "Failed to createScreenCaptureAndroid," |
| + " Maybe android version is too low"; |
| + callback.Run(false); |
| + return; |
| + } |
| + |
| + DCHECK(params.requested_format.frame_size.GetArea()); |
| + DCHECK(!(params.requested_format.frame_size.width() % 2)); |
| + DCHECK(!(params.requested_format.frame_size.height() % 2)); |
| + |
| + const jboolean ret = Java_ScreenCapture_startPrompt( |
| + AttachCurrentThread(), j_capture_.obj(), |
| + params.requested_format.frame_size.width(), |
| + params.requested_format.frame_size.height()); |
| + |
| + callback.Run(ret); |
| +} |
| + |
| +void ScreenCaptureMachineAndroid::Stop(const base::Closure& callback) { |
| + Java_ScreenCapture_stopCapture(AttachCurrentThread(), j_capture_.obj()); |
| + |
| + callback.Run(); |
| +} |
| + |
| +// ScreenCapture on Android works in passive way and there will be no captured |
| +// frame when there is no update to the screen. When oracle asks capturing for |
| +// refresh, the cached last frame will be delivered. |
| +void ScreenCaptureMachineAndroid::MaybeCaptureForRefresh() { |
| + if (lastFrame_.get() == nullptr) |
| + return; |
| + |
| + OnIncomingFrameAvailable(lastFrame_->visible_data(VideoFrame::kYPlane), |
| + lastFrame_->stride(VideoFrame::kYPlane), |
| + lastFrame_->visible_data(VideoFrame::kUPlane), |
| + lastFrame_->stride(VideoFrame::kUPlane), |
| + lastFrame_->visible_data(VideoFrame::kVPlane), |
| + lastFrame_->stride(VideoFrame::kVPlane), |
| + lastFrame_->visible_rect().width(), |
| + lastFrame_->visible_rect().height(), |
| + base::TimeTicks::Now().ToInternalValue()); |
| +} |
| + |
| +void ScreenCaptureMachineAndroid::OnIncomingFrameAvailable(const uint8_t* y_src, |
| + int y_stride, |
| + const uint8_t* u_src, |
| + int u_stride, |
| + const uint8_t* v_src, |
| + int v_stride, |
| + int width, |
| + int height, |
| + int64_t timestamp) { |
| + const VideoCaptureOracle::Event event = VideoCaptureOracle::kCompositorUpdate; |
| + const uint64_t absolute_micro = |
| + timestamp / base::Time::kNanosecondsPerMicrosecond; |
| + const base::TimeTicks start_time = |
| + base::TimeTicks::FromInternalValue(absolute_micro); |
|
miu
2016/06/02 22:33:51
Never use To/FromInternalValue() in Chromium code,
braveyao
2016/06/08 20:39:33
Done.
|
| + scoped_refptr<VideoFrame> frame; |
| + ThreadSafeCaptureOracle::CaptureFrameCallback capture_frame_cb; |
| + |
| + if (!oracle_proxy_->ObserveEventAndDecideCapture( |
| + event, gfx::Rect(), start_time, &frame, &capture_frame_cb)) { |
| + return; |
| + } |
| + |
| + DCHECK(frame->format() == PIXEL_FORMAT_I420 || |
| + frame->format() == PIXEL_FORMAT_YV12); |
| + |
| + libyuv::I420Scale(y_src, y_stride, u_src, u_stride, v_src, v_stride, width, |
| + height, frame->visible_data(VideoFrame::kYPlane), |
| + frame->stride(VideoFrame::kYPlane), |
| + frame->visible_data(VideoFrame::kUPlane), |
| + frame->stride(VideoFrame::kUPlane), |
| + frame->visible_data(VideoFrame::kVPlane), |
| + frame->stride(VideoFrame::kVPlane), |
| + frame->visible_rect().width(), |
| + frame->visible_rect().height(), libyuv::kFilterBilinear); |
| + |
| + // Deliver the populated VideoFrame. |
| + capture_frame_cb.Run(frame, start_time, true); |
| + |
| + lastFrame_ = frame; |
| +} |
| + |
| +} // namespace media |