Home | History | Annotate | Download | only in plugin
      1 // Copyright 2014 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/plugin/media_source_video_renderer.h"
      6 
      7 #include <string.h>
      8 
      9 #include "base/callback_helpers.h"
     10 #include "base/logging.h"
     11 #include "base/time/time.h"
     12 #include "remoting/proto/video.pb.h"
     13 #include "remoting/protocol/session_config.h"
     14 #include "third_party/libwebm/source/mkvmuxer.hpp"
     15 
     16 namespace remoting {
     17 
     18 static int kFrameIntervalNs = 1000000;
     19 
     20 class MediaSourceVideoRenderer::VideoWriter : public mkvmuxer::IMkvWriter {
     21  public:
     22   typedef std::vector<uint8_t> DataBuffer;
     23 
     24   VideoWriter(const webrtc::DesktopSize& frame_size, const char* codec_id);
     25   virtual ~VideoWriter();
     26 
     27   const webrtc::DesktopSize& size() { return frame_size_; }
     28   int64_t last_frame_timestamp() { return timecode_ - kFrameIntervalNs; }
     29 
     30   // IMkvWriter interface.
     31   virtual mkvmuxer::int32 Write(const void* buf, mkvmuxer::uint32 len) OVERRIDE;
     32   virtual mkvmuxer::int64 Position() const OVERRIDE;
     33   virtual mkvmuxer::int32 Position(mkvmuxer::int64 position) OVERRIDE;
     34   virtual bool Seekable() const OVERRIDE;
     35   virtual void ElementStartNotify(mkvmuxer::uint64 element_id,
     36                                   mkvmuxer::int64 position) OVERRIDE;
     37 
     38   scoped_ptr<DataBuffer> OnVideoFrame(const std::string& video_data,
     39                                       bool keyframe);
     40 
     41  private:
     42   webrtc::DesktopSize frame_size_;
     43   const char* codec_id_;
     44 
     45   scoped_ptr<DataBuffer> output_data_;
     46   int64_t position_;
     47   scoped_ptr<mkvmuxer::Segment> segment_;
     48   int64_t timecode_;
     49 };
     50 
     51 MediaSourceVideoRenderer::VideoWriter::VideoWriter(
     52     const webrtc::DesktopSize& frame_size,
     53     const char* codec_id)
     54     : frame_size_(frame_size),
     55       codec_id_(codec_id),
     56       position_(0),
     57       timecode_(0) {
     58   segment_.reset(new mkvmuxer::Segment());
     59   segment_->Init(this);
     60   segment_->set_mode(mkvmuxer::Segment::kLive);
     61 
     62   // DateUTC is specified in nanoseconds from 0:00 on January 1st, 2001.
     63   base::Time::Exploded millennium_exploded;
     64   memset(&millennium_exploded, 0, sizeof(millennium_exploded));
     65   millennium_exploded.year = 2001;
     66   millennium_exploded.month = 1;
     67   millennium_exploded.day_of_month = 1;
     68   segment_->GetSegmentInfo()->set_date_utc(
     69       (base::Time::Now() - base::Time::FromUTCExploded(millennium_exploded))
     70           .InMicroseconds() *
     71       base::Time::kNanosecondsPerMicrosecond);
     72 
     73   uint64 crop_right = 0;
     74   int width = frame_size_.width();
     75   if (width % 2 == 1) {
     76     ++width;
     77     crop_right = 1;
     78   }
     79 
     80   uint64 crop_bottom = 0;
     81   int height = frame_size_.height();
     82   if (height % 2 == 1) {
     83     ++height;
     84     crop_bottom = 1;
     85   }
     86 
     87   segment_->AddVideoTrack(width, height, 1);
     88   mkvmuxer::VideoTrack* video_track =
     89       reinterpret_cast<mkvmuxer::VideoTrack*>(segment_->GetTrackByNumber(1));
     90   video_track->set_codec_id(codec_id_);
     91   video_track->set_crop_right(crop_right);
     92   video_track->set_crop_bottom(crop_bottom);
     93   video_track->set_frame_rate(base::Time::kNanosecondsPerSecond /
     94                               kFrameIntervalNs);
     95   video_track->set_default_duration(kFrameIntervalNs);
     96   mkvmuxer::SegmentInfo* const info = segment_->GetSegmentInfo();
     97   info->set_writing_app("ChromotingViewer");
     98   info->set_muxing_app("ChromotingViewer");
     99 }
    100 
    101 MediaSourceVideoRenderer::VideoWriter::~VideoWriter() {}
    102 
    103 mkvmuxer::int32 MediaSourceVideoRenderer::VideoWriter::Write(
    104     const void* buf,
    105     mkvmuxer::uint32 len) {
    106   output_data_->insert(output_data_->end(),
    107                        reinterpret_cast<const char*>(buf),
    108                        reinterpret_cast<const char*>(buf) + len);
    109   position_ += len;
    110   return 0;
    111 }
    112 
    113 mkvmuxer::int64 MediaSourceVideoRenderer::VideoWriter::Position() const {
    114   return position_;
    115 }
    116 
    117 mkvmuxer::int32 MediaSourceVideoRenderer::VideoWriter::Position(
    118     mkvmuxer::int64 position) {
    119   return -1;
    120 }
    121 
    122 bool MediaSourceVideoRenderer::VideoWriter::Seekable() const {
    123   return false;
    124 }
    125 
    126 void MediaSourceVideoRenderer::VideoWriter::ElementStartNotify(
    127     mkvmuxer::uint64 element_id,
    128     mkvmuxer::int64 position) {
    129 }
    130 
    131 scoped_ptr<MediaSourceVideoRenderer::VideoWriter::DataBuffer>
    132 MediaSourceVideoRenderer::VideoWriter::OnVideoFrame(
    133     const std::string& video_data,
    134     bool keyframe) {
    135   DCHECK(!output_data_);
    136 
    137   output_data_.reset(new DataBuffer());
    138   segment_->AddFrame(reinterpret_cast<const uint8_t*>(video_data.data()),
    139                      video_data.size(), 1, timecode_, keyframe);
    140   timecode_ += kFrameIntervalNs;
    141   return output_data_.Pass();
    142 }
    143 
    144 MediaSourceVideoRenderer::MediaSourceVideoRenderer(Delegate* delegate)
    145     : delegate_(delegate),
    146       codec_id_(mkvmuxer::Tracks::kVp8CodecId),
    147       latest_sequence_number_(0) {
    148 }
    149 
    150 MediaSourceVideoRenderer::~MediaSourceVideoRenderer() {}
    151 
    152 void MediaSourceVideoRenderer::Initialize(
    153     const protocol::SessionConfig& config) {
    154   switch (config.video_config().codec) {
    155     case protocol::ChannelConfig::CODEC_VP8:
    156       format_string_ = "video/webm; codecs=\"vp8\"";
    157       codec_id_ = mkvmuxer::Tracks::kVp8CodecId;
    158       break;
    159     case protocol::ChannelConfig::CODEC_VP9:
    160       format_string_ = "video/webm; codecs=\"vp9\"";
    161       codec_id_ = mkvmuxer::Tracks::kVp9CodecId;
    162       break;
    163     default:
    164       NOTREACHED();
    165   }
    166 }
    167 
    168 ChromotingStats* MediaSourceVideoRenderer::GetStats() {
    169   return &stats_;
    170 }
    171 
    172 void MediaSourceVideoRenderer::ProcessVideoPacket(
    173     scoped_ptr<VideoPacket> packet,
    174     const base::Closure& done) {
    175   base::ScopedClosureRunner done_runner(done);
    176 
    177   // Don't need to do anything if the packet is empty. Host sends empty video
    178   // packets when the screen is not changing.
    179   if (!packet->data().size())
    180     return;
    181 
    182   // Update statistics.
    183   stats_.video_frame_rate()->Record(1);
    184   stats_.video_bandwidth()->Record(packet->data().size());
    185   if (packet->has_capture_time_ms())
    186     stats_.video_capture_ms()->Record(packet->capture_time_ms());
    187   if (packet->has_encode_time_ms())
    188     stats_.video_encode_ms()->Record(packet->encode_time_ms());
    189   if (packet->has_client_sequence_number() &&
    190       packet->client_sequence_number() > latest_sequence_number_) {
    191     latest_sequence_number_ = packet->client_sequence_number();
    192     base::TimeDelta round_trip_latency =
    193         base::Time::Now() -
    194         base::Time::FromInternalValue(packet->client_sequence_number());
    195     stats_.round_trip_ms()->Record(round_trip_latency.InMilliseconds());
    196   }
    197 
    198   bool media_source_needs_reset = false;
    199 
    200   webrtc::DesktopSize frame_size(packet->format().screen_width(),
    201                                  packet->format().screen_height());
    202   if (!writer_ ||
    203       (!writer_->size().equals(frame_size) && !frame_size.is_empty())) {
    204     media_source_needs_reset = true;
    205     writer_.reset(new VideoWriter(frame_size, codec_id_));
    206     delegate_->OnMediaSourceReset(format_string_);
    207   }
    208 
    209   webrtc::DesktopVector frame_dpi(packet->format().x_dpi(),
    210                                   packet->format().y_dpi());
    211   if (media_source_needs_reset || !frame_dpi_.equals(frame_dpi)) {
    212     frame_dpi_ = frame_dpi;
    213     delegate_->OnMediaSourceSize(frame_size, frame_dpi);
    214   }
    215 
    216   // Update the desktop shape region.
    217   webrtc::DesktopRegion desktop_shape;
    218   if (packet->has_use_desktop_shape()) {
    219     for (int i = 0; i < packet->desktop_shape_rects_size(); ++i) {
    220       Rect remoting_rect = packet->desktop_shape_rects(i);
    221       desktop_shape.AddRect(webrtc::DesktopRect::MakeXYWH(
    222           remoting_rect.x(), remoting_rect.y(),
    223           remoting_rect.width(), remoting_rect.height()));
    224     }
    225   } else {
    226     // Fallback for the case when the host didn't include the desktop shape.
    227     desktop_shape =
    228         webrtc::DesktopRegion(webrtc::DesktopRect::MakeSize(frame_size));
    229   }
    230 
    231   if (!desktop_shape_.Equals(desktop_shape)) {
    232     desktop_shape_.Swap(&desktop_shape);
    233     delegate_->OnMediaSourceShape(desktop_shape_);
    234   }
    235 
    236   // First bit is set to 0 for key frames.
    237   bool keyframe = (packet->data()[0] & 1) == 0;
    238 
    239   scoped_ptr<VideoWriter::DataBuffer> buffer =
    240       writer_->OnVideoFrame(packet->data(), keyframe);
    241   delegate_->OnMediaSourceData(&(*(buffer->begin())), buffer->size(), keyframe);
    242 }
    243 
    244 }  // namespace remoting
    245