Home | History | Annotate | Download | only in jni
      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