OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2016 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/capture/content/android/screen_capture_machine_android.h" | |
6 | |
7 #include "base/android/context_utils.h" | |
8 #include "base/android/jni_android.h" | |
9 #include "base/android/scoped_java_ref.h" | |
10 #include "jni/ScreenCapture_jni.h" | |
11 #include "media/base/video_capture_types.h" | |
12 #include "media/base/yuv_convert.h" | |
13 #include "media/capture/content/video_capture_oracle.h" | |
14 #include "third_party/libyuv/include/libyuv.h" | |
15 | |
16 using base::android::AttachCurrentThread; | |
17 | |
18 namespace media { | |
19 | |
20 // static | |
21 bool ScreenCaptureMachineAndroid::RegisterScreenCaptureMachine(JNIEnv* env) { | |
22 return RegisterNativesImpl(env); | |
23 } | |
24 | |
25 ScreenCaptureMachineAndroid::ScreenCaptureMachineAndroid() {} | |
26 | |
27 ScreenCaptureMachineAndroid::~ScreenCaptureMachineAndroid() {} | |
28 | |
29 // static | |
30 ScopedJavaLocalRef<jobject> | |
31 ScreenCaptureMachineAndroid::createScreenCaptureMachineAndroid( | |
32 jlong nativeScreenCaptureMachineAndroid) { | |
33 return (Java_ScreenCapture_createScreenCaptureMachine( | |
34 AttachCurrentThread(), base::android::GetApplicationContext(), | |
35 nativeScreenCaptureMachineAndroid)); | |
36 } | |
37 | |
38 void ScreenCaptureMachineAndroid::OnRGBAFrameAvailable(JNIEnv* env, | |
39 jobject obj, | |
40 jobject buf, | |
41 jint row_stride, | |
42 jint left, | |
43 jint top, | |
44 jint width, | |
45 jint height, | |
46 jlong timestamp) { | |
47 uint8_t* const src = | |
48 reinterpret_cast<uint8_t*>(env->GetDirectBufferAddress(buf)); | |
49 CHECK(src); | |
50 | |
51 const VideoCaptureOracle::Event event = VideoCaptureOracle::kCompositorUpdate; | |
52 const uint64_t absolute_micro = | |
53 timestamp / base::Time::kNanosecondsPerMicrosecond; | |
54 const base::TimeTicks start_time = | |
55 base::TimeTicks() + base::TimeDelta::FromMicroseconds(absolute_micro); | |
56 scoped_refptr<VideoFrame> frame; | |
57 ThreadSafeCaptureOracle::CaptureFrameCallback capture_frame_cb; | |
58 | |
59 if (!oracle_proxy_->ObserveEventAndDecideCapture( | |
60 event, gfx::Rect(), start_time, &frame, &capture_frame_cb)) { | |
61 return; | |
62 } | |
63 | |
64 DCHECK(frame->format() == PIXEL_FORMAT_I420 || | |
65 frame->format() == PIXEL_FORMAT_YV12); | |
66 | |
67 const int offset = top * row_stride + left * 4; | |
68 // ABGR little endian (rgba in memory) to I420. | |
69 libyuv::ABGRToI420( | |
miu
2016/06/21 03:34:07
Now this method has become incorrect. The scaling
braveyao
2016/06/21 22:10:25
Done.
Misunderstood mcasas@. Thought ABGRToI420 c
mcasas
2016/06/21 22:49:04
What you need is a single step using libyuv::Conve
| |
70 src + offset, row_stride, frame->visible_data(VideoFrame::kYPlane), | |
71 frame->stride(VideoFrame::kYPlane), | |
72 frame->visible_data(VideoFrame::kUPlane), | |
73 frame->stride(VideoFrame::kUPlane), | |
74 frame->visible_data(VideoFrame::kVPlane), | |
75 frame->stride(VideoFrame::kVPlane), frame->visible_rect().width(), | |
76 frame->visible_rect().height()); | |
77 | |
78 capture_frame_cb.Run(frame, start_time, true); | |
79 | |
80 lastFrame_ = frame; | |
81 } | |
82 | |
83 void ScreenCaptureMachineAndroid::OnI420FrameAvailable(JNIEnv* env, | |
84 jobject obj, | |
85 jobject y_buffer, | |
86 jint y_stride, | |
87 jobject u_buffer, | |
88 jobject v_buffer, | |
89 jint uv_row_stride, | |
90 jint uv_pixel_stride, | |
91 jint left, | |
92 jint top, | |
93 jint width, | |
94 jint height, | |
95 jlong timestamp) { | |
96 uint8_t* const y_src = | |
97 reinterpret_cast<uint8_t*>(env->GetDirectBufferAddress(y_buffer)); | |
98 CHECK(y_src); | |
99 uint8_t* u_src = | |
100 reinterpret_cast<uint8_t*>(env->GetDirectBufferAddress(u_buffer)); | |
101 CHECK(u_src); | |
102 uint8_t* v_src = | |
103 reinterpret_cast<uint8_t*>(env->GetDirectBufferAddress(v_buffer)); | |
104 CHECK(v_src); | |
105 | |
106 // De-interleave the U and V planes into temporary buffers, if needed. | |
107 int uv_stride = uv_row_stride; | |
108 std::unique_ptr<uint8_t[]> u_tmp, v_tmp; | |
109 if (uv_pixel_stride != 1) { | |
110 // U and V planes are actually interleaved, unpack them here. | |
111 // TODO(braveyao): According to | |
112 // https://developer.android.com/reference/android/graphics/ImageFormat.html #YUV_420_888, | |
113 // how U and V planes are interlaced is not guaranteed, so there is no an | |
114 // existing libyuv function suitable for such a job. Filed a request at | |
115 // https://bugs.chromium.org/p/libyuv/issues/detail?id=604. Switch to new | |
116 // function when it's available. | |
117 const int uv_plane_len = (int)env->GetDirectBufferCapacity(u_buffer); | |
118 u_tmp.reset(new uint8_t[(uv_plane_len + 1) / uv_pixel_stride]); | |
119 v_tmp.reset(new uint8_t[(uv_plane_len + 1) / uv_pixel_stride]); | |
120 for (int index = 0; index * uv_pixel_stride <= uv_plane_len; index++) { | |
121 u_tmp[index] = u_src[index * uv_pixel_stride]; | |
122 v_tmp[index] = v_src[index * uv_pixel_stride]; | |
123 } | |
124 u_src = u_tmp.get(); | |
125 v_src = v_tmp.get(); | |
126 uv_stride /= uv_pixel_stride; | |
127 } | |
128 | |
129 const int y_offset = top * y_stride + left; | |
130 const int uv_offset = (top / 2) * uv_stride + left / 2; | |
131 OnIncomingFrameAvailable(y_src + y_offset, y_stride, u_src + uv_offset, | |
132 uv_stride, v_src + uv_offset, uv_stride, width, | |
133 height, static_cast<int64_t>(timestamp)); | |
134 } | |
135 | |
136 void ScreenCaptureMachineAndroid::OnActivityResult(JNIEnv* env, | |
137 jobject obj, | |
138 jboolean result) { | |
139 if (!result) { | |
140 oracle_proxy_->ReportError(FROM_HERE, "The user denied screen capture"); | |
141 return; | |
142 } | |
143 | |
144 Java_ScreenCapture_startCapture(env, obj); | |
145 } | |
146 | |
147 void ScreenCaptureMachineAndroid::Start( | |
148 const scoped_refptr<ThreadSafeCaptureOracle>& oracle_proxy, | |
149 const VideoCaptureParams& params, | |
150 const base::Callback<void(bool)> callback) { | |
151 DCHECK(oracle_proxy.get()); | |
152 oracle_proxy_ = oracle_proxy; | |
153 | |
154 j_capture_.Reset( | |
155 createScreenCaptureMachineAndroid(reinterpret_cast<intptr_t>(this))); | |
156 | |
157 if (j_capture_.obj() == nullptr) { | |
158 DLOG(ERROR) << "Failed to createScreenCaptureAndroid"; | |
159 callback.Run(false); | |
160 return; | |
161 } | |
162 | |
163 DCHECK(params.requested_format.frame_size.GetArea()); | |
164 DCHECK(!(params.requested_format.frame_size.width() % 2)); | |
165 DCHECK(!(params.requested_format.frame_size.height() % 2)); | |
166 | |
167 const jboolean ret = Java_ScreenCapture_startPrompt( | |
168 AttachCurrentThread(), j_capture_.obj(), | |
169 params.requested_format.frame_size.width(), | |
170 params.requested_format.frame_size.height()); | |
171 | |
172 callback.Run(ret); | |
173 } | |
174 | |
175 void ScreenCaptureMachineAndroid::Stop(const base::Closure& callback) { | |
176 Java_ScreenCapture_stopCapture(AttachCurrentThread(), j_capture_.obj()); | |
177 | |
178 callback.Run(); | |
179 } | |
180 | |
181 // ScreenCapture on Android works in a passive way and there are no captured | |
182 // frames when there is no update to the screen. When the oracle asks for a | |
183 // capture refresh, the cached captured frame is redelivered. | |
184 void ScreenCaptureMachineAndroid::MaybeCaptureForRefresh() { | |
185 if (lastFrame_.get() == nullptr) | |
186 return; | |
187 | |
188 OnIncomingFrameAvailable(lastFrame_->visible_data(VideoFrame::kYPlane), | |
189 lastFrame_->stride(VideoFrame::kYPlane), | |
190 lastFrame_->visible_data(VideoFrame::kUPlane), | |
191 lastFrame_->stride(VideoFrame::kUPlane), | |
192 lastFrame_->visible_data(VideoFrame::kVPlane), | |
193 lastFrame_->stride(VideoFrame::kVPlane), | |
194 lastFrame_->visible_rect().width(), | |
195 lastFrame_->visible_rect().height(), | |
196 base::TimeTicks::Now().ToInternalValue()); | |
197 } | |
198 | |
199 void ScreenCaptureMachineAndroid::OnIncomingFrameAvailable(const uint8_t* y_src, | |
200 int y_stride, | |
201 const uint8_t* u_src, | |
202 int u_stride, | |
203 const uint8_t* v_src, | |
204 int v_stride, | |
205 int width, | |
206 int height, | |
207 int64_t timestamp) { | |
208 const VideoCaptureOracle::Event event = VideoCaptureOracle::kCompositorUpdate; | |
miu
2016/06/23 23:09:13
This is the wrong event for the MaybeCaptureForRef
braveyao
2016/06/24 22:46:11
Done.
| |
209 const uint64_t absolute_micro = | |
210 timestamp / base::Time::kNanosecondsPerMicrosecond; | |
211 const base::TimeTicks start_time = | |
212 base::TimeTicks() + base::TimeDelta::FromMicroseconds(absolute_micro); | |
213 scoped_refptr<VideoFrame> frame; | |
214 ThreadSafeCaptureOracle::CaptureFrameCallback capture_frame_cb; | |
215 | |
216 if (!oracle_proxy_->ObserveEventAndDecideCapture( | |
miu
2016/06/21 03:34:07
It would be better if this were executed before an
braveyao
2016/06/21 22:10:25
Yes it's true. But OnIncomingFrameAvailable will b
miu
2016/06/23 23:09:13
Normally I'd agree. However, IMHO, the negative pe
braveyao
2016/06/24 22:46:11
Done.
| |
217 event, gfx::Rect(), start_time, &frame, &capture_frame_cb)) { | |
218 return; | |
219 } | |
220 | |
221 DCHECK(frame->format() == PIXEL_FORMAT_I420 || | |
222 frame->format() == PIXEL_FORMAT_YV12); | |
223 | |
224 libyuv::I420Scale(y_src, y_stride, u_src, u_stride, v_src, v_stride, width, | |
225 height, frame->visible_data(VideoFrame::kYPlane), | |
226 frame->stride(VideoFrame::kYPlane), | |
227 frame->visible_data(VideoFrame::kUPlane), | |
228 frame->stride(VideoFrame::kUPlane), | |
229 frame->visible_data(VideoFrame::kVPlane), | |
230 frame->stride(VideoFrame::kVPlane), | |
231 frame->visible_rect().width(), | |
232 frame->visible_rect().height(), libyuv::kFilterBilinear); | |
233 | |
234 capture_frame_cb.Run(frame, start_time, true); | |
235 | |
236 lastFrame_ = frame; | |
237 } | |
miu
2016/06/21 03:34:07
I think the simplest solution to my above comments
braveyao
2016/06/21 22:10:25
Done to OnRGBAFrameAvailable and keep the current
| |
238 | |
239 } // namespace media | |
OLD | NEW |