Improve audio/video sync during underflow, reduce underflow frequency.
[chromium-blink-merge.git] / remoting / host / desktop_resizer_win.cc
blobde2fd37653901093ed3327dd3bc3401fc00c5968
1 // Copyright (c) 2012 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/host/desktop_resizer.h"
7 #include <map>
8 #include <windows.h>
10 #include "base/logging.h"
12 namespace {
13 // TODO(jamiewalch): Use the correct DPI for the mode: http://crbug.com/172405.
14 const int kDefaultDPI = 96;
15 } // namespace
17 namespace remoting {
19 // Provide comparison operation for ScreenResolution so we can use it in
20 // std::map.
21 static inline bool operator <(const ScreenResolution& a,
22 const ScreenResolution& b) {
23 if (a.dimensions().width() != b.dimensions().width())
24 return a.dimensions().width() < b.dimensions().width();
25 if (a.dimensions().height() != b.dimensions().height())
26 return a.dimensions().height() < b.dimensions().height();
27 if (a.dpi().x() != b.dpi().x())
28 return a.dpi().x() < b.dpi().x();
29 return a.dpi().y() < b.dpi().y();
32 class DesktopResizerWin : public DesktopResizer {
33 public:
34 DesktopResizerWin();
35 ~DesktopResizerWin() override;
37 // DesktopResizer interface.
38 ScreenResolution GetCurrentResolution() override;
39 std::list<ScreenResolution> GetSupportedResolutions(
40 const ScreenResolution& preferred) override;
41 void SetResolution(const ScreenResolution& resolution) override;
42 void RestoreResolution(const ScreenResolution& original) override;
44 private:
45 static bool IsResizeSupported();
47 // Calls EnumDisplaySettingsEx() for the primary monitor.
48 // Returns false if |mode_number| does not exist.
49 static bool GetPrimaryDisplayMode(
50 DWORD mode_number, DWORD flags, DEVMODE* mode);
52 // Returns true if the mode has width, height, bits-per-pixel, frequency
53 // and orientation fields.
54 static bool IsModeValid(const DEVMODE& mode);
56 // Returns the width & height of |mode|, or 0x0 if they are missing.
57 static ScreenResolution GetModeResolution(const DEVMODE& mode);
59 std::map<ScreenResolution, DEVMODE> best_mode_for_resolution_;
61 DISALLOW_COPY_AND_ASSIGN(DesktopResizerWin);
64 DesktopResizerWin::DesktopResizerWin() {
67 DesktopResizerWin::~DesktopResizerWin() {
70 ScreenResolution DesktopResizerWin::GetCurrentResolution() {
71 DEVMODE current_mode;
72 if (GetPrimaryDisplayMode(ENUM_CURRENT_SETTINGS, 0, &current_mode) &&
73 IsModeValid(current_mode))
74 return GetModeResolution(current_mode);
75 return ScreenResolution();
78 std::list<ScreenResolution> DesktopResizerWin::GetSupportedResolutions(
79 const ScreenResolution& preferred) {
80 if (!IsResizeSupported())
81 return std::list<ScreenResolution>();
83 // Enumerate the resolutions to return, and where there are multiple modes of
84 // the same resolution, store the one most closely matching the current mode
85 // in |best_mode_for_resolution_|.
86 DEVMODE current_mode;
87 if (!GetPrimaryDisplayMode(ENUM_CURRENT_SETTINGS, 0, &current_mode) ||
88 !IsModeValid(current_mode))
89 return std::list<ScreenResolution>();
91 std::list<ScreenResolution> resolutions;
92 best_mode_for_resolution_.clear();
93 for (DWORD i = 0; ; ++i) {
94 DEVMODE candidate_mode;
95 if (!GetPrimaryDisplayMode(i, EDS_ROTATEDMODE, &candidate_mode))
96 break;
98 // Ignore modes missing the fields that we expect.
99 if (!IsModeValid(candidate_mode))
100 continue;
102 // Ignore modes with differing bits-per-pixel.
103 if (candidate_mode.dmBitsPerPel != current_mode.dmBitsPerPel)
104 continue;
106 // If there are multiple modes with the same dimensions:
107 // - Prefer the modes which match the current rotation.
108 // - Among those, prefer modes which match the current frequency.
109 // - Otherwise, prefer modes with a higher frequency.
110 ScreenResolution candidate_resolution = GetModeResolution(candidate_mode);
111 if (best_mode_for_resolution_.count(candidate_resolution) != 0) {
112 DEVMODE best_mode = best_mode_for_resolution_[candidate_resolution];
114 if ((candidate_mode.dmDisplayOrientation !=
115 current_mode.dmDisplayOrientation) &&
116 (best_mode.dmDisplayOrientation ==
117 current_mode.dmDisplayOrientation)) {
118 continue;
121 if ((candidate_mode.dmDisplayFrequency !=
122 current_mode.dmDisplayFrequency) &&
123 (best_mode.dmDisplayFrequency >=
124 candidate_mode.dmDisplayFrequency)) {
125 continue;
127 } else {
128 // If we haven't seen this resolution before, add it to those we return.
129 resolutions.push_back(candidate_resolution);
132 best_mode_for_resolution_[candidate_resolution] = candidate_mode;
135 return resolutions;
138 void DesktopResizerWin::SetResolution(const ScreenResolution& resolution) {
139 if (best_mode_for_resolution_.count(resolution) == 0)
140 return;
142 DEVMODE new_mode = best_mode_for_resolution_[resolution];
143 DWORD result = ChangeDisplaySettings(&new_mode, CDS_FULLSCREEN);
144 if (result != DISP_CHANGE_SUCCESSFUL)
145 LOG(ERROR) << "SetResolution failed: " << result;
148 void DesktopResizerWin::RestoreResolution(const ScreenResolution& original) {
149 // Restore the display mode based on the registry configuration.
150 DWORD result = ChangeDisplaySettings(nullptr, 0);
151 if (result != DISP_CHANGE_SUCCESSFUL)
152 LOG(ERROR) << "RestoreResolution failed: " << result;
155 // static
156 bool DesktopResizerWin::IsResizeSupported() {
157 // Resize is supported only on single-monitor systems.
158 return GetSystemMetrics(SM_CMONITORS) == 1;
161 // static
162 bool DesktopResizerWin::GetPrimaryDisplayMode(
163 DWORD mode_number, DWORD flags, DEVMODE* mode) {
164 memset(mode, 0, sizeof(DEVMODE));
165 mode->dmSize = sizeof(DEVMODE);
166 if (!EnumDisplaySettingsEx(nullptr, mode_number, mode, flags))
167 return false;
168 return true;
171 // static
172 bool DesktopResizerWin::IsModeValid(const DEVMODE& mode) {
173 const DWORD kRequiredFields =
174 DM_PELSWIDTH | DM_PELSHEIGHT | DM_BITSPERPEL |
175 DM_DISPLAYFREQUENCY | DM_DISPLAYORIENTATION;
176 return (mode.dmFields & kRequiredFields) == kRequiredFields;
179 // static
180 ScreenResolution DesktopResizerWin::GetModeResolution(const DEVMODE& mode) {
181 DCHECK(IsModeValid(mode));
182 return ScreenResolution(
183 webrtc::DesktopSize(mode.dmPelsWidth, mode.dmPelsHeight),
184 webrtc::DesktopVector(kDefaultDPI, kDefaultDPI));
187 scoped_ptr<DesktopResizer> DesktopResizer::Create() {
188 return make_scoped_ptr(new DesktopResizerWin);
191 } // namespace remoting