Chromium Code Reviews| 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 |