Index: remoting/client/jni/jni_frame_consumer.cc |
diff --git a/remoting/client/jni/jni_frame_consumer.cc b/remoting/client/jni/jni_frame_consumer.cc |
index 36a81841771a0f9d9f4c537b3e653425ecff99db..1a560e93110f1a4f19ff28a9f371ccfd3572f597 100644 |
--- a/remoting/client/jni/jni_frame_consumer.cc |
+++ b/remoting/client/jni/jni_frame_consumer.cc |
@@ -6,57 +6,32 @@ |
#include "base/android/jni_android.h" |
#include "base/logging.h" |
+#include "base/stl_util.h" |
#include "base/synchronization/waitable_event.h" |
+#include "remoting/base/util.h" |
#include "remoting/client/frame_producer.h" |
#include "remoting/client/jni/chromoting_jni_runtime.h" |
#include "third_party/webrtc/modules/desktop_capture/desktop_frame.h" |
#include "third_party/webrtc/modules/desktop_capture/desktop_region.h" |
- |
-namespace { |
- |
-// Allocates its buffer within a Java direct byte buffer, where it can be |
-// accessed by both native and managed code. |
-class DirectDesktopFrame : public webrtc::BasicDesktopFrame { |
- public: |
- DirectDesktopFrame(int width, int height); |
- |
- virtual ~DirectDesktopFrame(); |
- |
- jobject buffer() const { |
- return buffer_; |
- } |
- |
- private: |
- jobject buffer_; |
-}; |
- |
-DirectDesktopFrame::DirectDesktopFrame(int width, int height) |
- : webrtc::BasicDesktopFrame(webrtc::DesktopSize(width, height)) { |
- JNIEnv* env = base::android::AttachCurrentThread(); |
- buffer_ = env->NewDirectByteBuffer(data(), stride()*height); |
-} |
- |
-DirectDesktopFrame::~DirectDesktopFrame() {} |
- |
-} // namespace |
+#include "ui/gfx/android/java_bitmap.h" |
namespace remoting { |
JniFrameConsumer::JniFrameConsumer(ChromotingJniRuntime* jni_runtime) |
: jni_runtime_(jni_runtime), |
- in_dtor_(false), |
frame_producer_(NULL) { |
} |
JniFrameConsumer::~JniFrameConsumer() { |
- // Stop giving the producer a buffer to work with. |
- in_dtor_ = true; |
- |
- // Don't destroy the object until we've deleted the buffer. |
+ // The producer should now return any pending buffers. At this point, however, |
+ // ReturnBuffer() tasks scheduled by the producer will not be delivered, |
+ // so we free all the buffers once the producer's queue is empty. |
base::WaitableEvent done_event(true, false); |
frame_producer_->RequestReturnBuffers( |
base::Bind(&base::WaitableEvent::Signal, base::Unretained(&done_event))); |
done_event.Wait(); |
+ |
+ STLDeleteElements(&buffers_); |
} |
void JniFrameConsumer::set_frame_producer(FrameProducer* producer) { |
@@ -69,29 +44,40 @@ void JniFrameConsumer::ApplyBuffer(const webrtc::DesktopSize& view_size, |
const webrtc::DesktopRegion& region) { |
DCHECK(jni_runtime_->display_task_runner()->BelongsToCurrentThread()); |
- scoped_ptr<webrtc::DesktopFrame> buffer_scoped(buffer); |
- jni_runtime_->RedrawCanvas(); |
- |
- if (view_size.width() > view_size_.width() || |
- view_size.height() > view_size_.height()) { |
- LOG(INFO) << "Existing buffer is too small"; |
- view_size_ = view_size; |
+ if (!view_size_.equals(view_size)) { |
+ // Drop the frame, since the data belongs to the previous generation, |
+ // before SetSourceSize() called SetOutputSizeAndClip(). |
+ FreeBuffer(buffer); |
+ return; |
+ } |
- // Manually destroy the old buffer before allocating a new one to prevent |
- // our memory footprint from temporarily ballooning. |
- buffer_scoped.reset(); |
- AllocateBuffer(); |
+ // Copy pixels from |buffer| into the Java Bitmap. |
+ // TODO(lambroslambrou): Optimize away this copy by having the VideoDecoder |
+ // decode directly into the Bitmap's pixel memory. This currently doesn't |
+ // work very well because the VideoDecoder writes the decoded data in BGRA, |
+ // and then the R/B channels are swapped in place (on the decoding thread). |
+ // If a repaint is triggered from a Java event handler, the unswapped pixels |
+ // can sometimes appear on the display. |
+ uint8* dest_buffer = static_cast<uint8*>(bitmap_->pixels()); |
+ webrtc::DesktopRect buffer_rect = webrtc::DesktopRect::MakeSize(view_size); |
+ |
+ for (webrtc::DesktopRegion::Iterator i(region); !i.IsAtEnd(); i.Advance()) { |
+ const webrtc::DesktopRect& rect(i.rect()); |
+ CopyRGB32Rect(buffer->data(), buffer->stride(), buffer_rect, dest_buffer, |
+ bitmap_->stride(), buffer_rect, rect); |
} |
+ // TODO(lambroslambrou): Optimize this by only repainting the changed pixels. |
+ jni_runtime_->RedrawCanvas(); |
+ |
// Supply |frame_producer_| with a buffer to render the next frame into. |
- if (!in_dtor_) |
- frame_producer_->DrawBuffer(buffer_scoped.release()); |
+ frame_producer_->DrawBuffer(buffer); |
} |
void JniFrameConsumer::ReturnBuffer(webrtc::DesktopFrame* buffer) { |
DCHECK(jni_runtime_->display_task_runner()->BelongsToCurrentThread()); |
LOG(INFO) << "Returning image buffer"; |
- delete buffer; |
+ FreeBuffer(buffer); |
} |
void JniFrameConsumer::SetSourceSize(const webrtc::DesktopSize& source_size, |
@@ -104,9 +90,8 @@ void JniFrameConsumer::SetSourceSize(const webrtc::DesktopSize& source_size, |
clip_area_ = webrtc::DesktopRect::MakeSize(view_size_); |
frame_producer_->SetOutputSizeAndClip(view_size_, clip_area_); |
- // Unless being destructed, allocate buffer and start drawing frames onto it. |
- frame_producer_->RequestReturnBuffers(base::Bind( |
- &JniFrameConsumer::AllocateBuffer, base::Unretained(this))); |
+ // Allocate buffer and start drawing frames onto it. |
+ AllocateBuffer(); |
} |
FrameConsumer::PixelFormat JniFrameConsumer::GetPixelFormat() { |
@@ -114,25 +99,29 @@ FrameConsumer::PixelFormat JniFrameConsumer::GetPixelFormat() { |
} |
void JniFrameConsumer::AllocateBuffer() { |
- // Only do anything if we're not being destructed. |
- if (!in_dtor_) { |
- if (!jni_runtime_->display_task_runner()->BelongsToCurrentThread()) { |
- jni_runtime_->display_task_runner()->PostTask(FROM_HERE, |
- base::Bind(&JniFrameConsumer::AllocateBuffer, |
- base::Unretained(this))); |
- return; |
- } |
- |
- DirectDesktopFrame* buffer = new DirectDesktopFrame(view_size_.width(), |
- view_size_.height()); |
- |
- // Update Java's reference to the buffer and record of its dimensions. |
- jni_runtime_->UpdateImageBuffer(view_size_.width(), |
- view_size_.height(), |
- buffer->buffer()); |
- |
- frame_producer_->DrawBuffer(buffer); |
- } |
+ DCHECK(jni_runtime_->display_task_runner()->BelongsToCurrentThread()); |
+ |
+ webrtc::DesktopSize size(view_size_.width(), view_size_.height()); |
+ |
+ // Allocate a new Bitmap, store references here, and pass it to Java. |
+ JNIEnv* env = base::android::AttachCurrentThread(); |
+ |
+ // |bitmap_| must be deleted before |bitmap_global_ref_| is released. |
+ bitmap_.reset(); |
+ bitmap_global_ref_.Reset(env, jni_runtime_->NewBitmap(size).obj()); |
+ bitmap_.reset(new gfx::JavaBitmap(bitmap_global_ref_.obj())); |
+ jni_runtime_->UpdateFrameBitmap(bitmap_global_ref_.obj()); |
+ |
+ webrtc::DesktopFrame* buffer = new webrtc::BasicDesktopFrame(size); |
+ buffers_.push_back(buffer); |
+ frame_producer_->DrawBuffer(buffer); |
+} |
+ |
+void JniFrameConsumer::FreeBuffer(webrtc::DesktopFrame* buffer) { |
+ DCHECK(std::find(buffers_.begin(), buffers_.end(), buffer) != buffers_.end()); |
+ |
+ buffers_.remove(buffer); |
+ delete buffer; |
} |
} // namespace remoting |