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.
5 #include "remoting/client/plugin/media_source_video_renderer.h"
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"
18 static int kFrameIntervalNs = 1000000;
20 class MediaSourceVideoRenderer::VideoWriter : public mkvmuxer::IMkvWriter {
22 typedef std::vector<uint8_t> DataBuffer;
24 explicit VideoWriter(const webrtc::DesktopSize& frame_size);
25 virtual ~VideoWriter();
27 const webrtc::DesktopSize& size() { return frame_size_; }
28 int64_t last_frame_timestamp() { return timecode_ - kFrameIntervalNs; }
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;
38 scoped_ptr<DataBuffer> OnVideoFrame(const std::string& video_data);
41 webrtc::DesktopSize frame_size_;
42 scoped_ptr<DataBuffer> output_data_;
44 scoped_ptr<mkvmuxer::Segment> segment_;
48 MediaSourceVideoRenderer::VideoWriter::VideoWriter(
49 const webrtc::DesktopSize& frame_size)
50 : frame_size_(frame_size),
53 segment_.reset(new mkvmuxer::Segment());
55 segment_->set_mode(mkvmuxer::Segment::kLive);
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))
66 base::Time::kNanosecondsPerMicrosecond);
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 /
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");
79 MediaSourceVideoRenderer::VideoWriter::~VideoWriter() {}
81 mkvmuxer::int32 MediaSourceVideoRenderer::VideoWriter::Write(
83 mkvmuxer::uint32 len) {
84 output_data_->insert(output_data_->end(),
85 reinterpret_cast<const char*>(buf),
86 reinterpret_cast<const char*>(buf) + len);
91 mkvmuxer::int64 MediaSourceVideoRenderer::VideoWriter::Position() const {
95 mkvmuxer::int32 MediaSourceVideoRenderer::VideoWriter::Position(
96 mkvmuxer::int64 position) {
100 bool MediaSourceVideoRenderer::VideoWriter::Seekable() const {
104 void MediaSourceVideoRenderer::VideoWriter::ElementStartNotify(
105 mkvmuxer::uint64 element_id,
106 mkvmuxer::int64 position) {
109 scoped_ptr<MediaSourceVideoRenderer::VideoWriter::DataBuffer>
110 MediaSourceVideoRenderer::VideoWriter::OnVideoFrame(
111 const std::string& video_data) {
112 DCHECK(!output_data_);
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();
122 MediaSourceVideoRenderer::MediaSourceVideoRenderer(Delegate* delegate)
123 : delegate_(delegate),
124 latest_sequence_number_(0) {
127 MediaSourceVideoRenderer::~MediaSourceVideoRenderer() {}
129 void MediaSourceVideoRenderer::Initialize(
130 const protocol::SessionConfig& config) {
131 DCHECK_EQ(config.video_config().codec, protocol::ChannelConfig::CODEC_VP8);
134 ChromotingStats* MediaSourceVideoRenderer::GetStats() {
138 void MediaSourceVideoRenderer::ProcessVideoPacket(
139 scoped_ptr<VideoPacket> packet,
140 const base::Closure& done) {
141 base::ScopedClosureRunner done_runner(done);
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())
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 =
160 base::Time::FromInternalValue(packet->client_sequence_number());
161 stats_.round_trip_ms()->Record(round_trip_latency.InMilliseconds());
164 bool media_source_needs_reset = false;
166 webrtc::DesktopSize frame_size(packet->format().screen_width(),
167 packet->format().screen_height());
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\"");
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);
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()));
192 // Fallback for the case when the host didn't include the desktop shape.
194 webrtc::DesktopRegion(webrtc::DesktopRect::MakeSize(frame_size));
197 if (!desktop_shape_.Equals(desktop_shape)) {
198 desktop_shape_.Swap(&desktop_shape);
199 delegate_->OnMediaSourceShape(desktop_shape_);
202 scoped_ptr<VideoWriter::DataBuffer> buffer =
203 writer_->OnVideoFrame(packet->data());
204 delegate_->OnMediaSourceData(&(*(buffer->begin())), buffer->size());
207 } // namespace remoting