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 DCHECK(jni_runtime_->display_task_runner()->BelongsToCurrentThread()); 50 51 if (!view_size_.equals(view_size)) { 52 // Drop the frame, since the data belongs to the previous generation, 53 // before SetSourceSize() called SetOutputSizeAndClip(). 54 FreeBuffer(buffer); 55 return; 56 } 57 58 // Copy pixels from |buffer| into the Java Bitmap. 59 // TODO(lambroslambrou): Optimize away this copy by having the VideoDecoder 60 // decode directly into the Bitmap's pixel memory. This currently doesn't 61 // work very well because the VideoDecoder writes the decoded data in BGRA, 62 // and then the R/B channels are swapped in place (on the decoding thread). 63 // If a repaint is triggered from a Java event handler, the unswapped pixels 64 // can sometimes appear on the display. 65 uint8* dest_buffer = static_cast<uint8*>(bitmap_->pixels()); 66 webrtc::DesktopRect buffer_rect = webrtc::DesktopRect::MakeSize(view_size); 67 68 for (webrtc::DesktopRegion::Iterator i(region); !i.IsAtEnd(); i.Advance()) { 69 const webrtc::DesktopRect& rect(i.rect()); 70 CopyRGB32Rect(buffer->data(), buffer->stride(), buffer_rect, dest_buffer, 71 bitmap_->stride(), buffer_rect, rect); 72 } 73 74 // TODO(lambroslambrou): Optimize this by only repainting the changed pixels. 75 base::TimeTicks start_time = base::TimeTicks::Now(); 76 jni_runtime_->RedrawCanvas(); 77 jni_instance_->RecordPaintTime( 78 (base::TimeTicks::Now() - start_time).InMilliseconds()); 79 80 // Supply |frame_producer_| with a buffer to render the next frame into. 81 frame_producer_->DrawBuffer(buffer); 82 } 83 84 void JniFrameConsumer::ReturnBuffer(webrtc::DesktopFrame* buffer) { 85 DCHECK(jni_runtime_->display_task_runner()->BelongsToCurrentThread()); 86 VLOG(0) << "Returning image buffer"; 87 FreeBuffer(buffer); 88 } 89 90 void JniFrameConsumer::SetSourceSize(const webrtc::DesktopSize& source_size, 91 const webrtc::DesktopVector& dpi) { 92 DCHECK(jni_runtime_->display_task_runner()->BelongsToCurrentThread()); 93 94 // We currently render the desktop 1:1 and perform pan/zoom scaling 95 // and cropping on the managed canvas. 96 view_size_ = source_size; 97 clip_area_ = webrtc::DesktopRect::MakeSize(view_size_); 98 frame_producer_->SetOutputSizeAndClip(view_size_, clip_area_); 99 100 // Allocate buffer and start drawing frames onto it. 101 AllocateBuffer(); 102 } 103 104 FrameConsumer::PixelFormat JniFrameConsumer::GetPixelFormat() { 105 return FORMAT_RGBA; 106 } 107 108 void JniFrameConsumer::AllocateBuffer() { 109 DCHECK(jni_runtime_->display_task_runner()->BelongsToCurrentThread()); 110 111 webrtc::DesktopSize size(view_size_.width(), view_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