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..cdc4f132e2d6b3724163c3cb633ba9b0ab0709ad |
--- /dev/null |
+++ b/media/capture/content/android/screen_capture_machine_android.cc |
@@ -0,0 +1,218 @@ |
+// 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 rowStride, |
+ jlong timestamp) { |
+ const VideoCaptureOracle::Event event = VideoCaptureOracle::kCompositorUpdate; |
+ uint64_t absolute_micro = |
+ static_cast<int64>(timestamp / base::Time::kNanosecondsPerMicrosecond); |
+ const base::TimeTicks start_time = |
+ base::TimeTicks::FromInternalValue(absolute_micro); |
+ scoped_refptr<VideoFrame> frame; |
+ ThreadSafeCaptureOracle::CaptureFrameCallback capture_frame_cb; |
+ |
+ bool oracle_decision = oracle_proxy_->ObserveEventAndDecideCapture( |
+ event, gfx::Rect(), start_time, &frame, &capture_frame_cb); |
+ |
+ if (!oracle_decision) |
+ return; |
+ |
+ uint8* source_data = |
+ reinterpret_cast<uint8_t*>(env->GetDirectBufferAddress(buf)); |
+ int source_stride = rowStride; |
mcasas
2016/05/19 19:11:41
Why not use |rowStride| directly? Consider renamin
braveyao
2016/05/20 22:27:25
Because the stride might be changed below at l.73.
|
+ |
+ // If needed, scale the captured frame to the required size. |
+ if (gfx::Size(width, height) != frame->visible_rect().size()) { |
+ std::unique_ptr<uint8[]> scaled_frame_data; |
+ scaled_frame_data.reset(new uint8[frame->visible_rect().width() * |
+ frame->visible_rect().height() * 4]); |
+ libyuv::ARGBScale( |
+ source_data, source_stride, width, height, scaled_frame_data.get(), |
+ frame->visible_rect().width() * 4, frame->visible_rect().width(), |
+ frame->visible_rect().height(), libyuv::kFilterBilinear); |
+ source_data = scaled_frame_data.get(); |
+ source_stride = frame->visible_rect().width() * 4; |
+ } |
+ |
+ DCHECK(frame->format() == PIXEL_FORMAT_I420 || |
+ frame->format() == PIXEL_FORMAT_YV12); |
+ |
+ ConvertRGB32ToYUV( |
mcasas
2016/05/19 19:11:41
Why not use libyuv here as well [1] ?
[1] https:/
braveyao
2016/05/20 22:27:25
Done.
|
+ source_data, frame->visible_data(VideoFrame::kYPlane), |
+ frame->visible_data(VideoFrame::kUPlane), |
+ frame->visible_data(VideoFrame::kVPlane), frame->visible_rect().width(), |
+ frame->visible_rect().height(), source_stride, |
+ frame->stride(VideoFrame::kYPlane), frame->stride(VideoFrame::kUPlane)); |
mcasas
2016/05/19 19:11:41
It'd be possible that the if{} in l.64-74 would
be
braveyao
2016/05/20 22:27:25
Refactored the processing here. The previous l.64-
|
+ |
+ // Deliver the populated VideoFrame. |
+ capture_frame_cb.Run(frame, start_time, true); |
+ |
+ lastFrame_ = frame; |
+} |
+ |
+void ScreenCaptureMachineAndroid::OnI420FrameAvailable(JNIEnv* env, |
+ jobject obj, |
+ jobject y_buffer, |
+ jint y_stride, |
+ jobject u_buffer, |
+ jint u_stride, |
+ jobject v_buffer, |
+ jint v_stride, |
+ jint width, |
+ jint height, |
+ jlong timestamp) { |
+ const VideoCaptureOracle::Event event = VideoCaptureOracle::kCompositorUpdate; |
+ uint64_t absolute_micro = |
mcasas
2016/05/19 19:11:41
const
braveyao
2016/05/20 22:27:25
Done.
|
+ static_cast<int64>(timestamp / base::Time::kNanosecondsPerMicrosecond); |
+ const base::TimeTicks start_time = |
+ base::TimeTicks::FromInternalValue(absolute_micro); |
+ scoped_refptr<VideoFrame> frame; |
+ ThreadSafeCaptureOracle::CaptureFrameCallback capture_frame_cb; |
+ |
+ bool oracle_decision = oracle_proxy_->ObserveEventAndDecideCapture( |
mcasas
2016/05/19 19:11:41
const? Or fold it into l.114.
braveyao
2016/05/20 22:27:26
Done.
|
+ event, gfx::Rect(), start_time, &frame, &capture_frame_cb); |
+ |
+ if (!oracle_decision) |
+ return; |
+ |
+ uint8 *y_src, *u_src, *v_src; |
+ y_src = reinterpret_cast<uint8_t*>(env->GetDirectBufferAddress(y_buffer)); |
+ CHECK(y_src); |
+ u_src = reinterpret_cast<uint8_t*>(env->GetDirectBufferAddress(u_buffer)); |
+ CHECK(u_src); |
+ v_src = reinterpret_cast<uint8_t*>(env->GetDirectBufferAddress(v_buffer)); |
+ CHECK(v_src); |
+ |
+ // U and V planes have |pixelStride| = 2, unpack them here. |
+ int uv_plane_len = (int)env->GetDirectBufferCapacity(u_buffer); |
+ for (int index = 0; index * 2 <= uv_plane_len; index++) { |
+ u_src[index] = u_src[index * 2]; |
+ v_src[index] = v_src[index * 2]; |
+ } |
+ |
+ 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; |
+} |
+ |
+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() == NULL) { |
mcasas
2016/05/19 19:11:41
s/NULL/nullptr/
braveyao
2016/05/20 22:27:26
Done.
|
+ LOG(ERROR) << "Failed to createScreenCaptureAndroid," |
+ " Maybe android version is too low"; |
+ callback.Run(false); |
+ return; |
+ } |
+ |
+ DCHECK(params.requested_format.frame_size.GetArea() > 0); |
mcasas
2016/05/19 19:11:41
No need for |> 0|.
braveyao
2016/05/20 22:27:25
Done.
|
+ DCHECK(!(params.requested_format.frame_size.width() % 2)); |
+ DCHECK(!(params.requested_format.frame_size.height() % 2)); |
+ |
+ jboolean ret = Java_ScreenCapture_startPrompt( |
mcasas
2016/05/19 19:11:41
const
braveyao
2016/05/20 22:27:26
Done.
|
+ AttachCurrentThread(), j_capture_.obj(), |
+ params.requested_format.frame_size.width(), |
+ params.requested_format.frame_size.height()); |
+ if (!ret) { |
mcasas
2016/05/19 19:11:41
Substitute l.184-189 with
callback.Run(ret);
braveyao
2016/05/20 22:27:26
Done.
|
+ callback.Run(false); |
+ return; |
+ } |
+ |
+ callback.Run(true); |
+} |
+ |
+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() == NULL) |
mcasas
2016/05/19 19:11:41
s/NULL/nullptr/
I think you can also say if(!last
braveyao
2016/05/20 22:27:26
Done.
|
+ return; |
+ |
+ const VideoCaptureOracle::Event event = VideoCaptureOracle::kCompositorUpdate; |
+ const base::TimeTicks start_time = base::TimeTicks::Now(); |
+ scoped_refptr<VideoFrame> frame; |
+ ThreadSafeCaptureOracle::CaptureFrameCallback capture_frame_cb; |
+ |
+ if (oracle_proxy_->ObserveEventAndDecideCapture( |
+ event, gfx::Rect(), start_time, &frame, &capture_frame_cb)) { |
+ // Deliver the last VideoFrame. |
mcasas
2016/05/19 19:11:41
nit: Suggest something like:
// Deliver again the
braveyao
2016/05/20 22:27:25
Done.
|
+ frame = lastFrame_; |
+ capture_frame_cb.Run(frame, start_time, true); |
+ } |
+} |
+ |
+} // namespace media |