Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / remoting / client / plugin / media_source_video_renderer.cc
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   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
48 MediaSourceVideoRenderer::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   segment_->AddVideoTrack(frame_size_.width(), frame_size_.height(), 1);
69   mkvmuxer::VideoTrack* video_track =
70       reinterpret_cast<mkvmuxer::VideoTrack*>(segment_->GetTrackByNumber(1));
71   video_track->set_frame_rate(base::Time::kNanosecondsPerSecond /
72                               kFrameIntervalNs);
73   video_track->set_default_duration(base::Time::kNanosecondsPerSecond);
74   mkvmuxer::SegmentInfo* const info = segment_->GetSegmentInfo();
75   info->set_writing_app("ChromotingViewer");
76   info->set_muxing_app("ChromotingViewer");
77 }
78
79 MediaSourceVideoRenderer::VideoWriter::~VideoWriter() {}
80
81 mkvmuxer::int32 MediaSourceVideoRenderer::VideoWriter::Write(
82     const void* buf,
83     mkvmuxer::uint32 len) {
84   output_data_->insert(output_data_->end(),
85                        reinterpret_cast<const char*>(buf),
86                        reinterpret_cast<const char*>(buf) + len);
87   position_ += len;
88   return 0;
89 }
90
91 mkvmuxer::int64 MediaSourceVideoRenderer::VideoWriter::Position() const {
92   return position_;
93 }
94
95 mkvmuxer::int32 MediaSourceVideoRenderer::VideoWriter::Position(
96     mkvmuxer::int64 position) {
97   return -1;
98 }
99
100 bool MediaSourceVideoRenderer::VideoWriter::Seekable() const {
101   return false;
102 }
103
104 void MediaSourceVideoRenderer::VideoWriter::ElementStartNotify(
105     mkvmuxer::uint64 element_id,
106     mkvmuxer::int64 position) {
107 }
108
109 scoped_ptr<MediaSourceVideoRenderer::VideoWriter::DataBuffer>
110 MediaSourceVideoRenderer::VideoWriter::OnVideoFrame(
111     const std::string& video_data) {
112   DCHECK(!output_data_);
113
114   output_data_.reset(new DataBuffer());
115   bool first_frame = (timecode_ == 0);
116   segment_->AddFrame(reinterpret_cast<const uint8_t*>(video_data.data()),
117                      video_data.size(), 1, timecode_, first_frame);
118   timecode_ += kFrameIntervalNs;
119   return output_data_.Pass();
120 }
121
122 MediaSourceVideoRenderer::MediaSourceVideoRenderer(Delegate* delegate)
123     : delegate_(delegate),
124       latest_sequence_number_(0) {
125 }
126
127 MediaSourceVideoRenderer::~MediaSourceVideoRenderer() {}
128
129 void MediaSourceVideoRenderer::Initialize(
130     const protocol::SessionConfig& config) {
131   DCHECK_EQ(config.video_config().codec, protocol::ChannelConfig::CODEC_VP8);
132 }
133
134 ChromotingStats* MediaSourceVideoRenderer::GetStats() {
135   return &stats_;
136 }
137
138 void MediaSourceVideoRenderer::ProcessVideoPacket(
139     scoped_ptr<VideoPacket> packet,
140     const base::Closure& done) {
141   base::ScopedClosureRunner done_runner(done);
142
143   // Don't need to do anything if the packet is empty. Host sends empty video
144   // packets when the screen is not changing.
145   if (!packet->data().size())
146     return;
147
148   // Update statistics.
149   stats_.video_frame_rate()->Record(1);
150   stats_.video_bandwidth()->Record(packet->data().size());
151   if (packet->has_capture_time_ms())
152     stats_.video_capture_ms()->Record(packet->capture_time_ms());
153   if (packet->has_encode_time_ms())
154     stats_.video_encode_ms()->Record(packet->encode_time_ms());
155   if (packet->has_client_sequence_number() &&
156       packet->client_sequence_number() > latest_sequence_number_) {
157     latest_sequence_number_ = packet->client_sequence_number();
158     base::TimeDelta round_trip_latency =
159         base::Time::Now() -
160         base::Time::FromInternalValue(packet->client_sequence_number());
161     stats_.round_trip_ms()->Record(round_trip_latency.InMilliseconds());
162   }
163
164   bool media_source_needs_reset = false;
165
166   webrtc::DesktopSize frame_size(packet->format().screen_width(),
167                                  packet->format().screen_height());
168   if (!writer_ ||
169       (!writer_->size().equals(frame_size) && !frame_size.is_empty())) {
170     media_source_needs_reset = true;
171     writer_.reset(new VideoWriter(frame_size));
172     delegate_->OnMediaSourceReset("video/webm; codecs=\"vp8\"");
173   }
174
175   webrtc::DesktopVector frame_dpi(packet->format().x_dpi(),
176                                   packet->format().y_dpi());
177   if (media_source_needs_reset || !frame_dpi_.equals(frame_dpi)) {
178     frame_dpi_ = frame_dpi;
179     delegate_->OnMediaSourceSize(frame_size, frame_dpi);
180   }
181
182   // Update the desktop shape region.
183   webrtc::DesktopRegion desktop_shape;
184   if (packet->has_use_desktop_shape()) {
185     for (int i = 0; i < packet->desktop_shape_rects_size(); ++i) {
186       Rect remoting_rect = packet->desktop_shape_rects(i);
187       desktop_shape.AddRect(webrtc::DesktopRect::MakeXYWH(
188           remoting_rect.x(), remoting_rect.y(),
189           remoting_rect.width(), remoting_rect.height()));
190     }
191   } else {
192     // Fallback for the case when the host didn't include the desktop shape.
193     desktop_shape =
194         webrtc::DesktopRegion(webrtc::DesktopRect::MakeSize(frame_size));
195   }
196
197   if (!desktop_shape_.Equals(desktop_shape)) {
198     desktop_shape_.Swap(&desktop_shape);
199     delegate_->OnMediaSourceShape(desktop_shape_);
200   }
201
202   scoped_ptr<VideoWriter::DataBuffer> buffer =
203       writer_->OnVideoFrame(packet->data());
204   delegate_->OnMediaSourceData(&(*(buffer->begin())), buffer->size());
205 }
206
207 }  // namespace remoting