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 3 // found in the LICENSE file. 4 5 #include "remoting/client/jni/jni_frame_consumer.h" 6 7 #include "base/android/jni_android.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" 12 #include "remoting/client/frame_producer.h" 13 #include "remoting/client/jni/chromoting_jni_instance.h" 14 #include "remoting/client/jni/chromoting_jni_runtime.h" 15 #include "third_party/webrtc/modules/desktop_capture/desktop_frame.h" 16 #include "third_party/webrtc/modules/desktop_capture/desktop_region.h" 17 #include "ui/gfx/android/java_bitmap.h" 18 19 namespace remoting { 20 21 JniFrameConsumer::JniFrameConsumer( 22 ChromotingJniRuntime* jni_runtime, 23 scoped_refptr<ChromotingJniInstance> jni_instance) 24 : jni_runtime_(jni_runtime), 25 jni_instance_(jni_instance), 26 frame_producer_(NULL) { 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 52 if (bitmap_->size().width() != buffer->size().width() || 53 bitmap_->size().height() != buffer->size().height()) { 54 // Drop the frame, since the data belongs to the previous generation, 55 // before SetSourceSize() called SetOutputSizeAndClip(). 56 FreeBuffer(buffer); 57 return; 58 } 59 60 // Copy pixels from |buffer| into the Java Bitmap. 61 // TODO(lambroslambrou): Optimize away this copy by having the VideoDecoder 62 // decode directly into the Bitmap's pixel memory. This currently doesn't 63 // work very well because the VideoDecoder writes the decoded data in BGRA, 64 // and then the R/B channels are swapped in place (on the decoding thread). 65 // If a repaint is triggered from a Java event handler, the unswapped pixels 66 // can sometimes appear on the display. 67 uint8* dest_buffer = static_cast<uint8*>(bitmap_->pixels()); 68 webrtc::DesktopRect buffer_rect = webrtc::DesktopRect::MakeSize(view_size); 69 70 for (webrtc::DesktopRegion::Iterator i(region); !i.IsAtEnd(); i.Advance()) { 71 const webrtc::DesktopRect& rect(i.rect()); 72 CopyRGB32Rect(buffer->data(), buffer->stride(), buffer_rect, dest_buffer, 73 bitmap_->stride(), buffer_rect, rect); 74 } 75 76 // TODO(lambroslambrou): Optimize this by only repainting the changed pixels. 77 base::TimeTicks start_time = base::TimeTicks::Now(); 78 jni_runtime_->RedrawCanvas(); 79 jni_instance_->RecordPaintTime( 80 (base::TimeTicks::Now() - start_time).InMilliseconds()); 81 82 // Supply |frame_producer_| with a buffer to render the next frame into. 83 frame_producer_->DrawBuffer(buffer); 84 } 85 86 void JniFrameConsumer::ReturnBuffer(webrtc::DesktopFrame* buffer) { 87 DCHECK(jni_runtime_->display_task_runner()->BelongsToCurrentThread()); 88 FreeBuffer(buffer); 89 } 90 91 void JniFrameConsumer::SetSourceSize(const webrtc::DesktopSize& source_size, 92 const webrtc::DesktopVector& dpi) { 93 DCHECK(jni_runtime_->display_task_runner()->BelongsToCurrentThread()); 94 95 // We currently render the desktop 1:1 and perform pan/zoom scaling 96 // and cropping on the managed canvas. 97 clip_area_ = webrtc::DesktopRect::MakeSize(source_size); 98 frame_producer_->SetOutputSizeAndClip(source_size, clip_area_); 99 100 // Allocate buffer and start drawing frames onto it. 101 AllocateBuffer(source_size); 102 } 103 104 FrameConsumer::PixelFormat JniFrameConsumer::GetPixelFormat() { 105 return FORMAT_RGBA; 106 } 107 108 void JniFrameConsumer::AllocateBuffer(const webrtc::DesktopSize& source_size) { 109 DCHECK(jni_runtime_->display_task_runner()->BelongsToCurrentThread()); 110 111 webrtc::DesktopSize size(source_size.width(), source_size.height()); 112 113 // Allocate a new Bitmap, store references here, and pass it to Java. 114 JNIEnv* env = base::android::AttachCurrentThread(); 115 116 // |bitmap_| must be deleted before |bitmap_global_ref_| is released. 117 bitmap_.reset(); 118 bitmap_global_ref_.Reset(env, jni_runtime_->NewBitmap(size).obj()); 119 bitmap_.reset(new gfx::JavaBitmap(bitmap_global_ref_.obj())); 120 jni_runtime_->UpdateFrameBitmap(bitmap_global_ref_.obj()); 121 122 webrtc::DesktopFrame* buffer = new webrtc::BasicDesktopFrame(size); 123 buffers_.push_back(buffer); 124 frame_producer_->DrawBuffer(buffer); 125 } 126 127 void JniFrameConsumer::FreeBuffer(webrtc::DesktopFrame* buffer) { 128 DCHECK(std::find(buffers_.begin(), buffers_.end(), buffer) != buffers_.end()); 129 130 buffers_.remove(buffer); 131 delete buffer; 132 } 133 134 } // namespace remoting 135