media_source_video_renderer.cc revision 5d1f7b1de12d16ceb2c938c56701a3e8bfa558f7
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 "remoting/proto/video.pb.h" 12#include "remoting/protocol/session_config.h" 13#include "third_party/libwebm/source/mkvmuxer.hpp" 14 15namespace remoting { 16 17static int kFrameIntervalNs = 1000000; 18 19class MediaSourceVideoRenderer::VideoWriter : public mkvmuxer::IMkvWriter { 20 public: 21 typedef std::vector<uint8_t> DataBuffer; 22 23 VideoWriter(const webrtc::DesktopSize& frame_size); 24 virtual ~VideoWriter(); 25 26 const webrtc::DesktopSize& size() { return frame_size_; } 27 int64_t last_frame_timestamp() { return timecode_ - kFrameIntervalNs; } 28 29 // IMkvWriter interface. 30 virtual mkvmuxer::int32 Write(const void* buf, mkvmuxer::uint32 len) OVERRIDE; 31 virtual mkvmuxer::int64 Position() const OVERRIDE; 32 virtual mkvmuxer::int32 Position(mkvmuxer::int64 position) OVERRIDE; 33 virtual bool Seekable() const OVERRIDE; 34 virtual void ElementStartNotify(mkvmuxer::uint64 element_id, 35 mkvmuxer::int64 position) OVERRIDE; 36 37 scoped_ptr<DataBuffer> OnVideoFrame(const std::string& video_data); 38 39 private: 40 webrtc::DesktopSize frame_size_; 41 scoped_ptr<DataBuffer> output_data_; 42 int64_t position_; 43 scoped_ptr<mkvmuxer::Segment> segment_; 44 int64_t timecode_; 45}; 46 47MediaSourceVideoRenderer::VideoWriter::VideoWriter( 48 const webrtc::DesktopSize& frame_size) 49 : frame_size_(frame_size), 50 position_(0), 51 timecode_(0) { 52 segment_.reset(new mkvmuxer::Segment()); 53 segment_->Init(this); 54 segment_->set_mode(mkvmuxer::Segment::kLive); 55 segment_->AddVideoTrack(frame_size_.width(), frame_size_.height(), 1); 56 mkvmuxer::SegmentInfo* const info = segment_->GetSegmentInfo(); 57 info->set_writing_app("ChromotingViewer"); 58 info->set_muxing_app("ChromotingViewer"); 59} 60 61MediaSourceVideoRenderer::VideoWriter::~VideoWriter() {} 62 63mkvmuxer::int32 MediaSourceVideoRenderer::VideoWriter::Write( 64 const void* buf, 65 mkvmuxer::uint32 len) { 66 output_data_->insert(output_data_->end(), 67 reinterpret_cast<const char*>(buf), 68 reinterpret_cast<const char*>(buf) + len); 69 position_ += len; 70 return 0; 71} 72 73mkvmuxer::int64 MediaSourceVideoRenderer::VideoWriter::Position() const { 74 return position_; 75} 76 77mkvmuxer::int32 MediaSourceVideoRenderer::VideoWriter::Position( 78 mkvmuxer::int64 position) { 79 return -1; 80} 81 82bool MediaSourceVideoRenderer::VideoWriter::Seekable() const { 83 return false; 84} 85 86void MediaSourceVideoRenderer::VideoWriter::ElementStartNotify( 87 mkvmuxer::uint64 element_id, 88 mkvmuxer::int64 position) { 89} 90 91scoped_ptr<MediaSourceVideoRenderer::VideoWriter::DataBuffer> 92MediaSourceVideoRenderer::VideoWriter::OnVideoFrame( 93 const std::string& video_data) { 94 DCHECK(!output_data_); 95 96 output_data_.reset(new DataBuffer()); 97 bool first_frame = (timecode_ == 0); 98 segment_->AddFrame(reinterpret_cast<const uint8_t*>(video_data.data()), 99 video_data.size(), 1, timecode_, first_frame); 100 timecode_ += kFrameIntervalNs; 101 return output_data_.Pass(); 102} 103 104MediaSourceVideoRenderer::MediaSourceVideoRenderer(Delegate* delegate) 105 : delegate_(delegate), 106 latest_sequence_number_(0) { 107} 108 109MediaSourceVideoRenderer::~MediaSourceVideoRenderer() {} 110 111void MediaSourceVideoRenderer::Initialize( 112 const protocol::SessionConfig& config) { 113 DCHECK_EQ(config.video_config().codec, protocol::ChannelConfig::CODEC_VP8); 114} 115 116ChromotingStats* MediaSourceVideoRenderer::GetStats() { 117 return &stats_; 118} 119 120void MediaSourceVideoRenderer::ProcessVideoPacket( 121 scoped_ptr<VideoPacket> packet, 122 const base::Closure& done) { 123 base::ScopedClosureRunner done_runner(done); 124 125 // Don't need to do anything if the packet is empty. Host sends empty video 126 // packets when the screen is not changing. 127 if (!packet->data().size()) 128 return; 129 130 // Update statistics. 131 stats_.video_frame_rate()->Record(1); 132 stats_.video_bandwidth()->Record(packet->data().size()); 133 if (packet->has_capture_time_ms()) 134 stats_.video_capture_ms()->Record(packet->capture_time_ms()); 135 if (packet->has_encode_time_ms()) 136 stats_.video_encode_ms()->Record(packet->encode_time_ms()); 137 if (packet->has_client_sequence_number() && 138 packet->client_sequence_number() > latest_sequence_number_) { 139 latest_sequence_number_ = packet->client_sequence_number(); 140 base::TimeDelta round_trip_latency = 141 base::Time::Now() - 142 base::Time::FromInternalValue(packet->client_sequence_number()); 143 stats_.round_trip_ms()->Record(round_trip_latency.InMilliseconds()); 144 } 145 146 bool media_source_needs_reset = false; 147 148 webrtc::DesktopSize frame_size(packet->format().screen_width(), 149 packet->format().screen_height()); 150 if (!writer_ || 151 (!writer_->size().equals(frame_size) && !frame_size.is_empty())) { 152 media_source_needs_reset = true; 153 writer_.reset(new VideoWriter(frame_size)); 154 delegate_->OnMediaSourceReset("video/webm; codecs=\"vp8\""); 155 } 156 157 webrtc::DesktopVector frame_dpi(packet->format().x_dpi(), 158 packet->format().y_dpi()); 159 if (media_source_needs_reset || !frame_dpi_.equals(frame_dpi)) { 160 frame_dpi_ = frame_dpi; 161 delegate_->OnMediaSourceSize(frame_size, frame_dpi); 162 } 163 164 // Update the desktop shape region. 165 webrtc::DesktopRegion desktop_shape; 166 if (packet->has_use_desktop_shape()) { 167 for (int i = 0; i < packet->desktop_shape_rects_size(); ++i) { 168 Rect remoting_rect = packet->desktop_shape_rects(i); 169 desktop_shape.AddRect(webrtc::DesktopRect::MakeXYWH( 170 remoting_rect.x(), remoting_rect.y(), 171 remoting_rect.width(), remoting_rect.height())); 172 } 173 } else { 174 // Fallback for the case when the host didn't include the desktop shape. 175 desktop_shape = 176 webrtc::DesktopRegion(webrtc::DesktopRect::MakeSize(frame_size)); 177 } 178 179 if (!desktop_shape_.Equals(desktop_shape)) { 180 desktop_shape_.Swap(&desktop_shape); 181 delegate_->OnMediaSourceShape(desktop_shape_); 182 } 183 184 scoped_ptr<VideoWriter::DataBuffer> buffer = 185 writer_->OnVideoFrame(packet->data()); 186 delegate_->OnMediaSourceData(&(*(buffer->begin())), buffer->size()); 187} 188 189} // namespace remoting 190