Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "remoting/client/jni/jni_frame_consumer.h" | 5 #include "remoting/client/jni/jni_frame_consumer.h" |
| 6 | 6 |
| 7 #include "base/android/jni_android.h" | 7 #include "base/android/jni_android.h" |
| 8 #include "base/logging.h" | 8 #include "base/logging.h" |
| 9 #include "base/stl_util.h" | |
| 10 #include "base/synchronization/waitable_event.h" | |
| 11 #include "remoting/base/util.h" | 9 #include "remoting/base/util.h" |
| 12 #include "remoting/client/frame_producer.h" | |
| 13 #include "remoting/client/jni/chromoting_jni_instance.h" | 10 #include "remoting/client/jni/chromoting_jni_instance.h" |
| 14 #include "remoting/client/jni/chromoting_jni_runtime.h" | 11 #include "remoting/client/jni/chromoting_jni_runtime.h" |
| 15 #include "third_party/webrtc/modules/desktop_capture/desktop_frame.h" | 12 #include "third_party/webrtc/modules/desktop_capture/desktop_frame.h" |
| 16 #include "third_party/webrtc/modules/desktop_capture/desktop_region.h" | 13 #include "third_party/webrtc/modules/desktop_capture/desktop_region.h" |
| 17 #include "ui/gfx/android/java_bitmap.h" | 14 #include "ui/gfx/android/java_bitmap.h" |
| 18 | 15 |
| 19 namespace remoting { | 16 namespace remoting { |
| 20 | 17 |
| 21 JniFrameConsumer::JniFrameConsumer( | 18 class JniFrameConsumer::Renderer { |
| 22 ChromotingJniRuntime* jni_runtime, | 19 public: |
| 23 scoped_refptr<ChromotingJniInstance> jni_instance) | 20 Renderer(ChromotingJniRuntime* jni_runtime) : jni_runtime_(jni_runtime) {} |
| 24 : jni_runtime_(jni_runtime), | 21 ~Renderer() { |
| 25 jni_instance_(jni_instance), | 22 DCHECK(jni_runtime_->network_task_runner()->BelongsToCurrentThread()); |
| 26 frame_producer_(nullptr) { | |
| 27 } | |
| 28 | |
| 29 JniFrameConsumer::~JniFrameConsumer() { | |
| 30 // The producer should now return any pending buffers. At this point, however, | |
| 31 // ReturnBuffer() tasks scheduled by the producer will not be delivered, | |
| 32 // so we free all the buffers once the producer's queue is empty. | |
| 33 base::WaitableEvent done_event(true, false); | |
| 34 frame_producer_->RequestReturnBuffers( | |
| 35 base::Bind(&base::WaitableEvent::Signal, base::Unretained(&done_event))); | |
| 36 done_event.Wait(); | |
| 37 | |
| 38 STLDeleteElements(&buffers_); | |
| 39 } | |
| 40 | |
| 41 void JniFrameConsumer::set_frame_producer(FrameProducer* producer) { | |
| 42 frame_producer_ = producer; | |
| 43 } | |
| 44 | |
| 45 void JniFrameConsumer::ApplyBuffer(const webrtc::DesktopSize& view_size, | |
| 46 const webrtc::DesktopRect& clip_area, | |
| 47 webrtc::DesktopFrame* buffer, | |
| 48 const webrtc::DesktopRegion& region, | |
| 49 const webrtc::DesktopRegion* shape) { | |
| 50 DCHECK(jni_runtime_->display_task_runner()->BelongsToCurrentThread()); | |
| 51 DCHECK(!shape); | |
| 52 | |
| 53 if (bitmap_->size().width() != buffer->size().width() || | |
| 54 bitmap_->size().height() != buffer->size().height()) { | |
| 55 // Drop the frame, since the data belongs to the previous generation, | |
| 56 // before SetSourceSize() called SetOutputSizeAndClip(). | |
| 57 FreeBuffer(buffer); | |
| 58 return; | |
| 59 } | 23 } |
| 60 | 24 |
| 61 // Copy pixels from |buffer| into the Java Bitmap. | 25 void RenderFrame(scoped_ptr<webrtc::DesktopFrame> frame); |
| 26 | |
| 27 private: | |
| 28 // Used to obtain task runner references and make calls to Java methods. | |
| 29 ChromotingJniRuntime* jni_runtime_; | |
| 30 | |
| 31 // This global reference is required, instead of a local reference, so it | |
| 32 // remains valid for the lifetime of |bitmap_| - gfx::JavaBitmap does not | |
| 33 // create its own global reference internally. And this global ref must be | |
| 34 // destroyed (released) after |bitmap_| is destroyed. | |
| 35 base::android::ScopedJavaGlobalRef<jobject> bitmap_global_ref_; | |
| 36 | |
| 37 // Reference to the frame bitmap that is passed to Java when the frame is | |
| 38 // allocated. This provides easy access to the underlying pixels. | |
| 39 scoped_ptr<gfx::JavaBitmap> bitmap_; | |
| 40 }; | |
| 41 | |
| 42 // Function called on the display thread to render the frame. | |
| 43 void JniFrameConsumer::Renderer::RenderFrame( | |
| 44 scoped_ptr<webrtc::DesktopFrame> frame) { | |
| 45 DCHECK(jni_runtime_->display_task_runner()->BelongsToCurrentThread()); | |
| 46 | |
| 47 if (!bitmap_ || bitmap_->size().width() != frame->size().width() || | |
| 48 bitmap_->size().height() != frame->size().height()) { | |
| 49 // Allocate a new Bitmap, store references here, and pass it to Java. | |
| 50 JNIEnv* env = base::android::AttachCurrentThread(); | |
| 51 | |
| 52 // |bitmap_| must be deleted before |bitmap_global_ref_| is released. | |
| 53 bitmap_.reset(); | |
| 54 bitmap_global_ref_.Reset( | |
| 55 env, | |
| 56 jni_runtime_->NewBitmap(frame->size().width(), frame->size().height()) | |
| 57 .obj()); | |
| 58 bitmap_.reset(new gfx::JavaBitmap(bitmap_global_ref_.obj())); | |
| 59 jni_runtime_->UpdateFrameBitmap(bitmap_global_ref_.obj()); | |
| 60 } | |
| 61 | |
| 62 // Copy pixels from |frame| into the Java Bitmap. | |
| 62 // TODO(lambroslambrou): Optimize away this copy by having the VideoDecoder | 63 // TODO(lambroslambrou): Optimize away this copy by having the VideoDecoder |
| 63 // decode directly into the Bitmap's pixel memory. This currently doesn't | 64 // decode directly into the Bitmap's pixel memory. This currently doesn't |
| 64 // work very well because the VideoDecoder writes the decoded data in BGRA, | 65 // work very well because the VideoDecoder writes the decoded data in BGRA, |
| 65 // and then the R/B channels are swapped in place (on the decoding thread). | 66 // and then the R/B channels are swapped in place (on the decoding thread). |
| 66 // If a repaint is triggered from a Java event handler, the unswapped pixels | 67 // If a repaint is triggered from a Java event handler, the unswapped pixels |
| 67 // can sometimes appear on the display. | 68 // can sometimes appear on the display. |
| 68 uint8* dest_buffer = static_cast<uint8*>(bitmap_->pixels()); | 69 uint8* dest_buffer = static_cast<uint8*>(bitmap_->pixels()); |
| 69 webrtc::DesktopRect buffer_rect = webrtc::DesktopRect::MakeSize(view_size); | 70 webrtc::DesktopRect buffer_rect = |
| 70 | 71 webrtc::DesktopRect::MakeSize(frame->size()); |
| 71 for (webrtc::DesktopRegion::Iterator i(region); !i.IsAtEnd(); i.Advance()) { | 72 for (webrtc::DesktopRegion::Iterator i(frame->updated_region()); !i.IsAtEnd(); |
| 72 const webrtc::DesktopRect& rect(i.rect()); | 73 i.Advance()) { |
| 73 CopyRGB32Rect(buffer->data(), buffer->stride(), buffer_rect, dest_buffer, | 74 CopyRGB32Rect(frame->data(), frame->stride(), buffer_rect, dest_buffer, |
| 74 bitmap_->stride(), buffer_rect, rect); | 75 bitmap_->stride(), buffer_rect, i.rect()); |
| 75 } | 76 } |
| 76 | 77 |
| 77 // TODO(lambroslambrou): Optimize this by only repainting the changed pixels. | |
| 78 base::TimeTicks start_time = base::TimeTicks::Now(); | |
| 79 jni_runtime_->RedrawCanvas(); | 78 jni_runtime_->RedrawCanvas(); |
| 80 jni_instance_->RecordPaintTime( | |
| 81 (base::TimeTicks::Now() - start_time).InMilliseconds()); | |
| 82 | |
| 83 // Supply |frame_producer_| with a buffer to render the next frame into. | |
| 84 frame_producer_->DrawBuffer(buffer); | |
| 85 } | 79 } |
| 86 | 80 |
| 87 void JniFrameConsumer::ReturnBuffer(webrtc::DesktopFrame* buffer) { | 81 JniFrameConsumer::JniFrameConsumer( |
| 88 DCHECK(jni_runtime_->display_task_runner()->BelongsToCurrentThread()); | 82 ChromotingJniRuntime* jni_runtime) |
| 89 FreeBuffer(buffer); | 83 : jni_runtime_(jni_runtime), |
| 84 renderer_(new Renderer(jni_runtime)), | |
| 85 weak_factory_(this) {} | |
| 86 | |
| 87 JniFrameConsumer::~JniFrameConsumer() { | |
| 88 jni_runtime_->display_task_runner()->DeleteSoon(FROM_HERE, | |
| 89 renderer_.release()); | |
| 90 } | 90 } |
| 91 | 91 |
| 92 void JniFrameConsumer::SetSourceSize(const webrtc::DesktopSize& source_size, | 92 scoped_ptr<webrtc::DesktopFrame> JniFrameConsumer::AllocateFrame( |
| 93 const webrtc::DesktopVector& dpi) { | 93 const webrtc::DesktopSize& size) { |
| 94 DCHECK(jni_runtime_->display_task_runner()->BelongsToCurrentThread()); | 94 return make_scoped_ptr(new webrtc::BasicDesktopFrame(size)); |
| 95 } | |
| 95 | 96 |
| 96 // We currently render the desktop 1:1 and perform pan/zoom scaling | 97 void JniFrameConsumer::DrawFrame(scoped_ptr<webrtc::DesktopFrame> frame, |
| 97 // and cropping on the managed canvas. | 98 const base::Closure& done) { |
| 98 clip_area_ = webrtc::DesktopRect::MakeSize(source_size); | 99 DCHECK(jni_runtime_->network_task_runner()->BelongsToCurrentThread()); |
| 99 frame_producer_->SetOutputSizeAndClip(source_size, clip_area_); | |
| 100 | 100 |
| 101 // Allocate buffer and start drawing frames onto it. | 101 jni_runtime_->display_task_runner()->PostTaskAndReply( |
| 102 AllocateBuffer(source_size); | 102 FROM_HERE, |
| 103 base::Bind(&Renderer::RenderFrame, base::Unretained(renderer_.get()), | |
|
Jamie
2015/08/19 20:52:10
Why is base::Unretained safe here, when you're usi
Sergey Ulanov
2015/08/19 23:37:13
This task is posted for the renderer_ and the rend
| |
| 104 base::Passed(&frame)), | |
| 105 base::Bind(&JniFrameConsumer::OnFrameRendered, weak_factory_.GetWeakPtr(), | |
| 106 done)); | |
| 107 } | |
| 108 | |
| 109 void JniFrameConsumer::OnFrameRendered(const base::Closure& done) { | |
| 110 DCHECK(jni_runtime_->network_task_runner()->BelongsToCurrentThread()); | |
| 111 | |
| 112 done.Run() | |
| 103 } | 113 } |
| 104 | 114 |
| 105 FrameConsumer::PixelFormat JniFrameConsumer::GetPixelFormat() { | 115 FrameConsumer::PixelFormat JniFrameConsumer::GetPixelFormat() { |
| 106 return FORMAT_RGBA; | 116 return FORMAT_RGBA; |
| 107 } | 117 } |
| 108 | 118 |
| 109 void JniFrameConsumer::AllocateBuffer(const webrtc::DesktopSize& source_size) { | |
| 110 DCHECK(jni_runtime_->display_task_runner()->BelongsToCurrentThread()); | |
| 111 | |
| 112 webrtc::DesktopSize size(source_size.width(), source_size.height()); | |
| 113 | |
| 114 // Allocate a new Bitmap, store references here, and pass it to Java. | |
| 115 JNIEnv* env = base::android::AttachCurrentThread(); | |
| 116 | |
| 117 // |bitmap_| must be deleted before |bitmap_global_ref_| is released. | |
| 118 bitmap_.reset(); | |
| 119 bitmap_global_ref_.Reset(env, jni_runtime_->NewBitmap(size).obj()); | |
| 120 bitmap_.reset(new gfx::JavaBitmap(bitmap_global_ref_.obj())); | |
| 121 jni_runtime_->UpdateFrameBitmap(bitmap_global_ref_.obj()); | |
| 122 | |
| 123 webrtc::DesktopFrame* buffer = new webrtc::BasicDesktopFrame(size); | |
| 124 buffers_.push_back(buffer); | |
| 125 frame_producer_->DrawBuffer(buffer); | |
| 126 } | |
| 127 | |
| 128 void JniFrameConsumer::FreeBuffer(webrtc::DesktopFrame* buffer) { | |
| 129 DCHECK(std::find(buffers_.begin(), buffers_.end(), buffer) != buffers_.end()); | |
| 130 | |
| 131 buffers_.remove(buffer); | |
| 132 delete buffer; | |
| 133 } | |
| 134 | |
| 135 } // namespace remoting | 119 } // namespace remoting |
| OLD | NEW |