Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(20)

Side by Side Diff: remoting/client/jni/jni_frame_consumer.cc

Issue 24072012: Hold video frame in Bitmap instead of keeping a ByteBuffer reference. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Keep track of buffers in JniFrameConsumer Created 7 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « remoting/client/jni/jni_frame_consumer.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2013 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "remoting/client/jni/jni_frame_consumer.h" 5 #include "remoting/client/jni/jni_frame_consumer.h"
6 6
7 #include "base/android/jni_android.h" 7 #include "base/android/jni_android.h"
8 #include "base/logging.h" 8 #include "base/logging.h"
9 #include "base/stl_util.h"
9 #include "base/synchronization/waitable_event.h" 10 #include "base/synchronization/waitable_event.h"
11 #include "remoting/base/util.h"
10 #include "remoting/client/frame_producer.h" 12 #include "remoting/client/frame_producer.h"
11 #include "remoting/client/jni/chromoting_jni_runtime.h" 13 #include "remoting/client/jni/chromoting_jni_runtime.h"
12 #include "third_party/webrtc/modules/desktop_capture/desktop_frame.h" 14 #include "third_party/webrtc/modules/desktop_capture/desktop_frame.h"
13 #include "third_party/webrtc/modules/desktop_capture/desktop_region.h" 15 #include "third_party/webrtc/modules/desktop_capture/desktop_region.h"
14 16 #include "ui/gfx/android/java_bitmap.h"
15 namespace {
16
17 // Allocates its buffer within a Java direct byte buffer, where it can be
18 // accessed by both native and managed code.
19 class DirectDesktopFrame : public webrtc::BasicDesktopFrame {
20 public:
21 DirectDesktopFrame(int width, int height);
22
23 virtual ~DirectDesktopFrame();
24
25 jobject buffer() const {
26 return buffer_;
27 }
28
29 private:
30 jobject buffer_;
31 };
32
33 DirectDesktopFrame::DirectDesktopFrame(int width, int height)
34 : webrtc::BasicDesktopFrame(webrtc::DesktopSize(width, height)) {
35 JNIEnv* env = base::android::AttachCurrentThread();
36 buffer_ = env->NewDirectByteBuffer(data(), stride()*height);
37 }
38
39 DirectDesktopFrame::~DirectDesktopFrame() {}
40
41 } // namespace
42 17
43 namespace remoting { 18 namespace remoting {
44 19
45 JniFrameConsumer::JniFrameConsumer(ChromotingJniRuntime* jni_runtime) 20 JniFrameConsumer::JniFrameConsumer(ChromotingJniRuntime* jni_runtime)
46 : jni_runtime_(jni_runtime), 21 : jni_runtime_(jni_runtime),
47 in_dtor_(false),
48 frame_producer_(NULL) { 22 frame_producer_(NULL) {
49 } 23 }
50 24
51 JniFrameConsumer::~JniFrameConsumer() { 25 JniFrameConsumer::~JniFrameConsumer() {
52 // Stop giving the producer a buffer to work with. 26 // The producer should now return any pending buffers. At this point, however,
53 in_dtor_ = true; 27 // ReturnBuffer() tasks scheduled by the producer will not be delivered,
54 28 // so we free all the buffers once the producer's queue is empty.
55 // Don't destroy the object until we've deleted the buffer.
56 base::WaitableEvent done_event(true, false); 29 base::WaitableEvent done_event(true, false);
57 frame_producer_->RequestReturnBuffers( 30 frame_producer_->RequestReturnBuffers(
58 base::Bind(&base::WaitableEvent::Signal, base::Unretained(&done_event))); 31 base::Bind(&base::WaitableEvent::Signal, base::Unretained(&done_event)));
59 done_event.Wait(); 32 done_event.Wait();
33
34 STLDeleteElements(&buffers_);
60 } 35 }
61 36
62 void JniFrameConsumer::set_frame_producer(FrameProducer* producer) { 37 void JniFrameConsumer::set_frame_producer(FrameProducer* producer) {
63 frame_producer_ = producer; 38 frame_producer_ = producer;
64 } 39 }
65 40
66 void JniFrameConsumer::ApplyBuffer(const webrtc::DesktopSize& view_size, 41 void JniFrameConsumer::ApplyBuffer(const webrtc::DesktopSize& view_size,
67 const webrtc::DesktopRect& clip_area, 42 const webrtc::DesktopRect& clip_area,
68 webrtc::DesktopFrame* buffer, 43 webrtc::DesktopFrame* buffer,
69 const webrtc::DesktopRegion& region) { 44 const webrtc::DesktopRegion& region) {
70 DCHECK(jni_runtime_->display_task_runner()->BelongsToCurrentThread()); 45 DCHECK(jni_runtime_->display_task_runner()->BelongsToCurrentThread());
71 46
72 scoped_ptr<webrtc::DesktopFrame> buffer_scoped(buffer); 47 if (!view_size_.equals(view_size)) {
48 // Drop the frame, since the data belongs to the previous generation,
49 // before SetSourceSize() called SetOutputSizeAndClip().
50 FreeBuffer(buffer);
51 return;
52 }
53
54 // Copy pixels from |buffer| into the Java Bitmap.
55 // TODO(lambroslambrou): Optimize away this copy by having the VideoDecoder
56 // decode directly into the Bitmap's pixel memory. This currently doesn't
57 // work very well because the VideoDecoder writes the decoded data in BGRA,
58 // and then the R/B channels are swapped in place (on the decoding thread).
59 // If a repaint is triggered from a Java event handler, the unswapped pixels
60 // can sometimes appear on the display.
61 uint8* dest_buffer = static_cast<uint8*>(bitmap_->pixels());
62 webrtc::DesktopRect buffer_rect = webrtc::DesktopRect::MakeSize(view_size);
63
64 for (webrtc::DesktopRegion::Iterator i(region); !i.IsAtEnd(); i.Advance()) {
65 const webrtc::DesktopRect& rect(i.rect());
66 CopyRGB32Rect(buffer->data(), buffer->stride(), buffer_rect, dest_buffer,
67 bitmap_->stride(), buffer_rect, rect);
68 }
69
70 // TODO(lambroslambrou): Optimize this by only repainting the changed pixels.
73 jni_runtime_->RedrawCanvas(); 71 jni_runtime_->RedrawCanvas();
74 72
75 if (view_size.width() > view_size_.width() ||
76 view_size.height() > view_size_.height()) {
77 LOG(INFO) << "Existing buffer is too small";
78 view_size_ = view_size;
79
80 // Manually destroy the old buffer before allocating a new one to prevent
81 // our memory footprint from temporarily ballooning.
82 buffer_scoped.reset();
83 AllocateBuffer();
84 }
85
86 // Supply |frame_producer_| with a buffer to render the next frame into. 73 // Supply |frame_producer_| with a buffer to render the next frame into.
87 if (!in_dtor_) 74 frame_producer_->DrawBuffer(buffer);
88 frame_producer_->DrawBuffer(buffer_scoped.release());
89 } 75 }
90 76
91 void JniFrameConsumer::ReturnBuffer(webrtc::DesktopFrame* buffer) { 77 void JniFrameConsumer::ReturnBuffer(webrtc::DesktopFrame* buffer) {
92 DCHECK(jni_runtime_->display_task_runner()->BelongsToCurrentThread()); 78 DCHECK(jni_runtime_->display_task_runner()->BelongsToCurrentThread());
93 LOG(INFO) << "Returning image buffer"; 79 LOG(INFO) << "Returning image buffer";
94 delete buffer; 80 FreeBuffer(buffer);
95 } 81 }
96 82
97 void JniFrameConsumer::SetSourceSize(const webrtc::DesktopSize& source_size, 83 void JniFrameConsumer::SetSourceSize(const webrtc::DesktopSize& source_size,
98 const webrtc::DesktopVector& dpi) { 84 const webrtc::DesktopVector& dpi) {
99 DCHECK(jni_runtime_->display_task_runner()->BelongsToCurrentThread()); 85 DCHECK(jni_runtime_->display_task_runner()->BelongsToCurrentThread());
100 86
101 // We currently render the desktop 1:1 and perform pan/zoom scaling 87 // We currently render the desktop 1:1 and perform pan/zoom scaling
102 // and cropping on the managed canvas. 88 // and cropping on the managed canvas.
103 view_size_ = source_size; 89 view_size_ = source_size;
104 clip_area_ = webrtc::DesktopRect::MakeSize(view_size_); 90 clip_area_ = webrtc::DesktopRect::MakeSize(view_size_);
105 frame_producer_->SetOutputSizeAndClip(view_size_, clip_area_); 91 frame_producer_->SetOutputSizeAndClip(view_size_, clip_area_);
106 92
107 // Unless being destructed, allocate buffer and start drawing frames onto it. 93 // Allocate buffer and start drawing frames onto it.
108 frame_producer_->RequestReturnBuffers(base::Bind( 94 AllocateBuffer();
109 &JniFrameConsumer::AllocateBuffer, base::Unretained(this)));
110 } 95 }
111 96
112 FrameConsumer::PixelFormat JniFrameConsumer::GetPixelFormat() { 97 FrameConsumer::PixelFormat JniFrameConsumer::GetPixelFormat() {
113 return FORMAT_RGBA; 98 return FORMAT_RGBA;
114 } 99 }
115 100
116 void JniFrameConsumer::AllocateBuffer() { 101 void JniFrameConsumer::AllocateBuffer() {
117 // Only do anything if we're not being destructed. 102 DCHECK(jni_runtime_->display_task_runner()->BelongsToCurrentThread());
118 if (!in_dtor_) {
119 if (!jni_runtime_->display_task_runner()->BelongsToCurrentThread()) {
120 jni_runtime_->display_task_runner()->PostTask(FROM_HERE,
121 base::Bind(&JniFrameConsumer::AllocateBuffer,
122 base::Unretained(this)));
123 return;
124 }
125 103
126 DirectDesktopFrame* buffer = new DirectDesktopFrame(view_size_.width(), 104 webrtc::DesktopSize size(view_size_.width(), view_size_.height());
127 view_size_.height());
128 105
129 // Update Java's reference to the buffer and record of its dimensions. 106 // Allocate a new Bitmap, store references here, and pass it to Java.
130 jni_runtime_->UpdateImageBuffer(view_size_.width(), 107 JNIEnv* env = base::android::AttachCurrentThread();
131 view_size_.height(),
132 buffer->buffer());
133 108
134 frame_producer_->DrawBuffer(buffer); 109 // |bitmap_| must be deleted before |bitmap_global_ref_| is released.
135 } 110 bitmap_.reset();
111 bitmap_global_ref_.Reset(env, jni_runtime_->NewBitmap(size).obj());
112 bitmap_.reset(new gfx::JavaBitmap(bitmap_global_ref_.obj()));
113 jni_runtime_->UpdateFrameBitmap(bitmap_global_ref_.obj());
114
115 webrtc::DesktopFrame* buffer = new webrtc::BasicDesktopFrame(size);
116 buffers_.push_back(buffer);
117 frame_producer_->DrawBuffer(buffer);
118 }
119
120 void JniFrameConsumer::FreeBuffer(webrtc::DesktopFrame* buffer) {
121 DCHECK(std::find(buffers_.begin(), buffers_.end(), buffer) != buffers_.end());
122
123 buffers_.remove(buffer);
124 delete buffer;
136 } 125 }
137 126
138 } // namespace remoting 127 } // namespace remoting
OLDNEW
« no previous file with comments | « remoting/client/jni/jni_frame_consumer.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698