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