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/synchronization/waitable_event.h"
     10 #include "remoting/client/frame_producer.h"
     11 #include "remoting/client/jni/chromoting_jni_runtime.h"
     12 #include "third_party/webrtc/modules/desktop_capture/desktop_frame.h"
     13 
     14 namespace {
     15 
     16 // Allocates its buffer within a Java direct byte buffer, where it can be
     17 // accessed by both native and managed code.
     18 class DirectDesktopFrame : public webrtc::BasicDesktopFrame {
     19  public:
     20   DirectDesktopFrame(int width, int height);
     21 
     22   virtual ~DirectDesktopFrame();
     23 
     24   jobject buffer() const {
     25     return buffer_;
     26   }
     27 
     28  private:
     29   jobject buffer_;
     30 };
     31 
     32 DirectDesktopFrame::DirectDesktopFrame(int width, int height)
     33     : webrtc::BasicDesktopFrame(webrtc::DesktopSize(width, height)) {
     34   JNIEnv* env = base::android::AttachCurrentThread();
     35   buffer_ = env->NewDirectByteBuffer(data(), stride()*height);
     36 }
     37 
     38 DirectDesktopFrame::~DirectDesktopFrame() {}
     39 
     40 }  // namespace
     41 
     42 namespace remoting {
     43 
     44 JniFrameConsumer::JniFrameConsumer(ChromotingJniRuntime* jni_runtime)
     45     : jni_runtime_(jni_runtime),
     46       in_dtor_(false),
     47       frame_producer_(NULL) {
     48 }
     49 
     50 JniFrameConsumer::~JniFrameConsumer() {
     51   // Stop giving the producer a buffer to work with.
     52   in_dtor_ = true;
     53 
     54   // Don't destroy the object until we've deleted the buffer.
     55   base::WaitableEvent done_event(true, false);
     56   frame_producer_->RequestReturnBuffers(
     57       base::Bind(&base::WaitableEvent::Signal, base::Unretained(&done_event)));
     58   done_event.Wait();
     59 }
     60 
     61 void JniFrameConsumer::set_frame_producer(FrameProducer* producer) {
     62   frame_producer_ = producer;
     63 }
     64 
     65 void JniFrameConsumer::ApplyBuffer(const SkISize& view_size,
     66                                    const SkIRect& clip_area,
     67                                    webrtc::DesktopFrame* buffer,
     68                                    const SkRegion& region) {
     69   DCHECK(jni_runtime_->display_task_runner()->BelongsToCurrentThread());
     70 
     71   scoped_ptr<webrtc::DesktopFrame> buffer_scoped(buffer);
     72   jni_runtime_->RedrawCanvas();
     73 
     74   if (view_size.width() > view_size_.width() ||
     75       view_size.height() > view_size_.height()) {
     76     LOG(INFO) << "Existing buffer is too small";
     77     view_size_ = view_size;
     78 
     79     // Manually destroy the old buffer before allocating a new one to prevent
     80     // our memory footprint from temporarily ballooning.
     81     buffer_scoped.reset();
     82     AllocateBuffer();
     83   }
     84 
     85   // Supply |frame_producer_| with a buffer to render the next frame into.
     86   if (!in_dtor_)
     87     frame_producer_->DrawBuffer(buffer_scoped.release());
     88 }
     89 
     90 void JniFrameConsumer::ReturnBuffer(webrtc::DesktopFrame* buffer) {
     91   DCHECK(jni_runtime_->display_task_runner()->BelongsToCurrentThread());
     92   LOG(INFO) << "Returning image buffer";
     93   delete buffer;
     94 }
     95 
     96 void JniFrameConsumer::SetSourceSize(const SkISize& source_size,
     97                                      const SkIPoint& dpi) {
     98   DCHECK(jni_runtime_->display_task_runner()->BelongsToCurrentThread());
     99 
    100   // We currently render the desktop 1:1 and perform pan/zoom scaling
    101   // and cropping on the managed canvas.
    102   view_size_ = source_size;
    103   clip_area_ = SkIRect::MakeSize(view_size_);
    104   frame_producer_->SetOutputSizeAndClip(view_size_, clip_area_);
    105 
    106   // Unless being destructed, allocate buffer and start drawing frames onto it.
    107   frame_producer_->RequestReturnBuffers(base::Bind(
    108       &JniFrameConsumer::AllocateBuffer, base::Unretained(this)));
    109 }
    110 
    111 void JniFrameConsumer::AllocateBuffer() {
    112   // Only do anything if we're not being destructed.
    113   if (!in_dtor_) {
    114     if (!jni_runtime_->display_task_runner()->BelongsToCurrentThread()) {
    115       jni_runtime_->display_task_runner()->PostTask(FROM_HERE,
    116           base::Bind(&JniFrameConsumer::AllocateBuffer,
    117                      base::Unretained(this)));
    118       return;
    119     }
    120 
    121     DirectDesktopFrame* buffer = new DirectDesktopFrame(view_size_.width(),
    122                                                         view_size_.height());
    123 
    124     // Update Java's reference to the buffer and record of its dimensions.
    125     jni_runtime_->UpdateImageBuffer(view_size_.width(),
    126                                         view_size_.height(),
    127                                         buffer->buffer());
    128 
    129     frame_producer_->DrawBuffer(buffer);
    130   }
    131 }
    132 
    133 }  // namespace remoting
    134