Home | History | Annotate | Download | only in client
      1 // Copyright (c) 2012 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/rectangle_update_decoder.h"
      6 
      7 #include "base/bind.h"
      8 #include "base/callback.h"
      9 #include "base/callback_helpers.h"
     10 #include "base/location.h"
     11 #include "base/logging.h"
     12 #include "base/single_thread_task_runner.h"
     13 #include "remoting/base/util.h"
     14 #include "remoting/client/frame_consumer.h"
     15 #include "remoting/codec/video_decoder.h"
     16 #include "remoting/codec/video_decoder_verbatim.h"
     17 #include "remoting/codec/video_decoder_vpx.h"
     18 #include "remoting/protocol/session_config.h"
     19 #include "third_party/libyuv/include/libyuv/convert_argb.h"
     20 #include "third_party/webrtc/modules/desktop_capture/desktop_frame.h"
     21 
     22 using base::Passed;
     23 using remoting::protocol::ChannelConfig;
     24 using remoting::protocol::SessionConfig;
     25 
     26 namespace remoting {
     27 
     28 // This class wraps a VideoDecoder and byte-swaps the pixels for compatibility
     29 // with the android.graphics.Bitmap class.
     30 // TODO(lambroslambrou): Refactor so that the VideoDecoder produces data
     31 // in the right byte-order, instead of swapping it here.
     32 class RgbToBgrVideoDecoderFilter : public VideoDecoder {
     33  public:
     34   RgbToBgrVideoDecoderFilter(scoped_ptr<VideoDecoder> parent)
     35       : parent_(parent.Pass()) {
     36   }
     37 
     38   virtual void Initialize(const webrtc::DesktopSize& screen_size) OVERRIDE {
     39     parent_->Initialize(screen_size);
     40   }
     41 
     42   virtual bool DecodePacket(const VideoPacket& packet) OVERRIDE {
     43     return parent_->DecodePacket(packet);
     44   }
     45 
     46   virtual void Invalidate(const webrtc::DesktopSize& view_size,
     47                           const webrtc::DesktopRegion& region) OVERRIDE {
     48     return parent_->Invalidate(view_size, region);
     49   }
     50 
     51   virtual void RenderFrame(const webrtc::DesktopSize& view_size,
     52                            const webrtc::DesktopRect& clip_area,
     53                            uint8* image_buffer,
     54                            int image_stride,
     55                            webrtc::DesktopRegion* output_region) OVERRIDE {
     56     parent_->RenderFrame(view_size, clip_area, image_buffer, image_stride,
     57                          output_region);
     58 
     59     for (webrtc::DesktopRegion::Iterator i(*output_region); !i.IsAtEnd();
     60          i.Advance()) {
     61       webrtc::DesktopRect rect = i.rect();
     62       uint8* pixels = image_buffer + (rect.top() * image_stride) +
     63         (rect.left() * kBytesPerPixel);
     64       libyuv::ABGRToARGB(pixels, image_stride, pixels, image_stride,
     65                          rect.width(), rect.height());
     66     }
     67   }
     68 
     69   virtual const webrtc::DesktopRegion* GetImageShape() OVERRIDE {
     70     return parent_->GetImageShape();
     71   }
     72 
     73  private:
     74   scoped_ptr<VideoDecoder> parent_;
     75 };
     76 
     77 RectangleUpdateDecoder::RectangleUpdateDecoder(
     78     scoped_refptr<base::SingleThreadTaskRunner> main_task_runner,
     79     scoped_refptr<base::SingleThreadTaskRunner> decode_task_runner,
     80     scoped_refptr<FrameConsumerProxy> consumer)
     81     : main_task_runner_(main_task_runner),
     82       decode_task_runner_(decode_task_runner),
     83       consumer_(consumer),
     84       paint_scheduled_(false),
     85       latest_sequence_number_(0) {
     86 }
     87 
     88 RectangleUpdateDecoder::~RectangleUpdateDecoder() {
     89 }
     90 
     91 void RectangleUpdateDecoder::Initialize(const SessionConfig& config) {
     92   if (!decode_task_runner_->BelongsToCurrentThread()) {
     93     decode_task_runner_->PostTask(
     94         FROM_HERE, base::Bind(&RectangleUpdateDecoder::Initialize, this,
     95                               config));
     96     return;
     97   }
     98 
     99   // Initialize decoder based on the selected codec.
    100   ChannelConfig::Codec codec = config.video_config().codec;
    101   if (codec == ChannelConfig::CODEC_VERBATIM) {
    102     decoder_.reset(new VideoDecoderVerbatim());
    103   } else if (codec == ChannelConfig::CODEC_VP8) {
    104     decoder_ = VideoDecoderVpx::CreateForVP8();
    105   } else if (codec == ChannelConfig::CODEC_VP9) {
    106     decoder_ = VideoDecoderVpx::CreateForVP9();
    107   } else {
    108     NOTREACHED() << "Invalid Encoding found: " << codec;
    109   }
    110 
    111   if (consumer_->GetPixelFormat() == FrameConsumer::FORMAT_RGBA) {
    112     scoped_ptr<VideoDecoder> wrapper(
    113         new RgbToBgrVideoDecoderFilter(decoder_.Pass()));
    114     decoder_ = wrapper.Pass();
    115   }
    116 }
    117 
    118 void RectangleUpdateDecoder::DecodePacket(scoped_ptr<VideoPacket> packet,
    119                                           const base::Closure& done) {
    120   DCHECK(decode_task_runner_->BelongsToCurrentThread());
    121 
    122   base::ScopedClosureRunner done_runner(done);
    123 
    124   bool decoder_needs_reset = false;
    125   bool notify_size_or_dpi_change = false;
    126 
    127   // If the packet includes screen size or DPI information, store them.
    128   if (packet->format().has_screen_width() &&
    129       packet->format().has_screen_height()) {
    130     webrtc::DesktopSize source_size(packet->format().screen_width(),
    131                                     packet->format().screen_height());
    132     if (!source_size_.equals(source_size)) {
    133       source_size_ = source_size;
    134       decoder_needs_reset = true;
    135       notify_size_or_dpi_change = true;
    136     }
    137   }
    138   if (packet->format().has_x_dpi() && packet->format().has_y_dpi()) {
    139     webrtc::DesktopVector source_dpi(packet->format().x_dpi(),
    140                                      packet->format().y_dpi());
    141     if (!source_dpi.equals(source_dpi_)) {
    142       source_dpi_ = source_dpi;
    143       notify_size_or_dpi_change = true;
    144     }
    145   }
    146 
    147   // If we've never seen a screen size, ignore the packet.
    148   if (source_size_.is_empty())
    149     return;
    150 
    151   if (decoder_needs_reset)
    152     decoder_->Initialize(source_size_);
    153   if (notify_size_or_dpi_change)
    154     consumer_->SetSourceSize(source_size_, source_dpi_);
    155 
    156   if (decoder_->DecodePacket(*packet.get())) {
    157     SchedulePaint();
    158   } else {
    159     LOG(ERROR) << "DecodePacket() failed.";
    160   }
    161 }
    162 
    163 void RectangleUpdateDecoder::SchedulePaint() {
    164   if (paint_scheduled_)
    165     return;
    166   paint_scheduled_ = true;
    167   decode_task_runner_->PostTask(
    168       FROM_HERE, base::Bind(&RectangleUpdateDecoder::DoPaint, this));
    169 }
    170 
    171 void RectangleUpdateDecoder::DoPaint() {
    172   DCHECK(paint_scheduled_);
    173   paint_scheduled_ = false;
    174 
    175   // If the view size is empty or we have no output buffers ready, return.
    176   if (buffers_.empty() || view_size_.is_empty())
    177     return;
    178 
    179   // If no Decoder is initialized, or the host dimensions are empty, return.
    180   if (!decoder_.get() || source_size_.is_empty())
    181     return;
    182 
    183   // Draw the invalidated region to the buffer.
    184   webrtc::DesktopFrame* buffer = buffers_.front();
    185   webrtc::DesktopRegion output_region;
    186   decoder_->RenderFrame(view_size_, clip_area_,
    187                         buffer->data(),
    188                         buffer->stride(),
    189                         &output_region);
    190 
    191   // Notify the consumer that painting is done.
    192   if (!output_region.is_empty()) {
    193     buffers_.pop_front();
    194     consumer_->ApplyBuffer(view_size_, clip_area_, buffer, output_region);
    195   }
    196 }
    197 
    198 void RectangleUpdateDecoder::RequestReturnBuffers(const base::Closure& done) {
    199   if (!decode_task_runner_->BelongsToCurrentThread()) {
    200     decode_task_runner_->PostTask(
    201         FROM_HERE, base::Bind(&RectangleUpdateDecoder::RequestReturnBuffers,
    202         this, done));
    203     return;
    204   }
    205 
    206   while (!buffers_.empty()) {
    207     consumer_->ReturnBuffer(buffers_.front());
    208     buffers_.pop_front();
    209   }
    210 
    211   if (!done.is_null())
    212     done.Run();
    213 }
    214 
    215 void RectangleUpdateDecoder::DrawBuffer(webrtc::DesktopFrame* buffer) {
    216   if (!decode_task_runner_->BelongsToCurrentThread()) {
    217     decode_task_runner_->PostTask(
    218         FROM_HERE, base::Bind(&RectangleUpdateDecoder::DrawBuffer,
    219                               this, buffer));
    220     return;
    221   }
    222 
    223   DCHECK(clip_area_.width() <= buffer->size().width() &&
    224          clip_area_.height() <= buffer->size().height());
    225 
    226   buffers_.push_back(buffer);
    227   SchedulePaint();
    228 }
    229 
    230 void RectangleUpdateDecoder::InvalidateRegion(
    231     const webrtc::DesktopRegion& region) {
    232   if (!decode_task_runner_->BelongsToCurrentThread()) {
    233     decode_task_runner_->PostTask(
    234         FROM_HERE, base::Bind(&RectangleUpdateDecoder::InvalidateRegion,
    235                               this, region));
    236     return;
    237   }
    238 
    239   if (decoder_.get()) {
    240     decoder_->Invalidate(view_size_, region);
    241     SchedulePaint();
    242   }
    243 }
    244 
    245 void RectangleUpdateDecoder::SetOutputSizeAndClip(
    246     const webrtc::DesktopSize& view_size,
    247     const webrtc::DesktopRect& clip_area) {
    248   if (!decode_task_runner_->BelongsToCurrentThread()) {
    249     decode_task_runner_->PostTask(
    250         FROM_HERE, base::Bind(&RectangleUpdateDecoder::SetOutputSizeAndClip,
    251                               this, view_size, clip_area));
    252     return;
    253   }
    254 
    255   // The whole frame needs to be repainted if the scaling factor has changed.
    256   if (!view_size_.equals(view_size) && decoder_.get()) {
    257     webrtc::DesktopRegion region;
    258     region.AddRect(webrtc::DesktopRect::MakeSize(view_size));
    259     decoder_->Invalidate(view_size, region);
    260   }
    261 
    262   if (!view_size_.equals(view_size) ||
    263       !clip_area_.equals(clip_area)) {
    264     view_size_ = view_size;
    265     clip_area_ = clip_area;
    266 
    267     // Return buffers that are smaller than needed to the consumer for
    268     // reuse/reallocation.
    269     std::list<webrtc::DesktopFrame*>::iterator i = buffers_.begin();
    270     while (i != buffers_.end()) {
    271       if ((*i)->size().width() < clip_area_.width() ||
    272           (*i)->size().height() < clip_area_.height()) {
    273         consumer_->ReturnBuffer(*i);
    274         i = buffers_.erase(i);
    275       } else {
    276         ++i;
    277       }
    278     }
    279 
    280     SchedulePaint();
    281   }
    282 }
    283 
    284 const webrtc::DesktopRegion* RectangleUpdateDecoder::GetBufferShape() {
    285   return decoder_->GetImageShape();
    286 }
    287 
    288 void RectangleUpdateDecoder::ProcessVideoPacket(scoped_ptr<VideoPacket> packet,
    289                                                 const base::Closure& done) {
    290   DCHECK(main_task_runner_->BelongsToCurrentThread());
    291 
    292   // If the video packet is empty then drop it. Empty packets are used to
    293   // maintain activity on the network.
    294   if (!packet->has_data() || packet->data().size() == 0) {
    295     done.Run();
    296     return;
    297   }
    298 
    299   // Add one frame to the counter.
    300   stats_.video_frame_rate()->Record(1);
    301 
    302   // Record other statistics received from host.
    303   stats_.video_bandwidth()->Record(packet->data().size());
    304   if (packet->has_capture_time_ms())
    305     stats_.video_capture_ms()->Record(packet->capture_time_ms());
    306   if (packet->has_encode_time_ms())
    307     stats_.video_encode_ms()->Record(packet->encode_time_ms());
    308   if (packet->has_client_sequence_number() &&
    309       packet->client_sequence_number() > latest_sequence_number_) {
    310     latest_sequence_number_ = packet->client_sequence_number();
    311     base::TimeDelta round_trip_latency =
    312         base::Time::Now() -
    313         base::Time::FromInternalValue(packet->client_sequence_number());
    314     stats_.round_trip_ms()->Record(round_trip_latency.InMilliseconds());
    315   }
    316 
    317   // Measure the latency between the last packet being received and presented.
    318   base::Time decode_start = base::Time::Now();
    319 
    320   base::Closure decode_done = base::Bind(
    321       &RectangleUpdateDecoder::OnPacketDone, this, decode_start, done);
    322 
    323   decode_task_runner_->PostTask(FROM_HERE, base::Bind(
    324       &RectangleUpdateDecoder::DecodePacket, this,
    325       base::Passed(&packet), decode_done));
    326 }
    327 
    328 void RectangleUpdateDecoder::OnPacketDone(base::Time decode_start,
    329                                           const base::Closure& done) {
    330   if (!main_task_runner_->BelongsToCurrentThread()) {
    331     main_task_runner_->PostTask(FROM_HERE, base::Bind(
    332         &RectangleUpdateDecoder::OnPacketDone, this,
    333         decode_start, done));
    334     return;
    335   }
    336 
    337   // Record the latency between the packet being received and presented.
    338   stats_.video_decode_ms()->Record(
    339       (base::Time::Now() - decode_start).InMilliseconds());
    340 
    341   done.Run();
    342 }
    343 
    344 ChromotingStats* RectangleUpdateDecoder::GetStats() {
    345   DCHECK(main_task_runner_->BelongsToCurrentThread());
    346   return &stats_;
    347 }
    348 
    349 }  // namespace remoting
    350