1 // Copyright 2016 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 "chrome/browser/media/webrtc/desktop_media_list_base.h"
6
7 #include <set>
8 #include <utility>
9
10 #include "base/bind.h"
11 #include "chrome/browser/media/webrtc/desktop_media_list.h"
12 #include "content/public/browser/browser_task_traits.h"
13 #include "content/public/browser/browser_thread.h"
14 #include "ui/gfx/image/image.h"
15
16 using content::DesktopMediaID;
17
DesktopMediaListBase(base::TimeDelta update_period)18 DesktopMediaListBase::DesktopMediaListBase(base::TimeDelta update_period)
19 : update_period_(update_period) {}
20
~DesktopMediaListBase()21 DesktopMediaListBase::~DesktopMediaListBase() {}
22
SetUpdatePeriod(base::TimeDelta period)23 void DesktopMediaListBase::SetUpdatePeriod(base::TimeDelta period) {
24 DCHECK(!observer_);
25 update_period_ = period;
26 }
27
SetThumbnailSize(const gfx::Size & thumbnail_size)28 void DesktopMediaListBase::SetThumbnailSize(const gfx::Size& thumbnail_size) {
29 thumbnail_size_ = thumbnail_size;
30 }
31
SetViewDialogWindowId(DesktopMediaID dialog_id)32 void DesktopMediaListBase::SetViewDialogWindowId(DesktopMediaID dialog_id) {
33 view_dialog_id_ = dialog_id;
34 }
35
StartUpdating(DesktopMediaListObserver * observer)36 void DesktopMediaListBase::StartUpdating(DesktopMediaListObserver* observer) {
37 DCHECK(!observer_);
38 observer_ = observer;
39
40 // Process sources previously discovered by a call to Update().
41 if (observer_) {
42 for (size_t i = 0; i < sources_.size(); i++) {
43 observer_->OnSourceAdded(this, i);
44 }
45 }
46
47 DCHECK(!refresh_callback_);
48 refresh_callback_ = base::BindOnce(&DesktopMediaListBase::ScheduleNextRefresh,
49 weak_factory_.GetWeakPtr());
50 Refresh(true);
51 }
52
Update(UpdateCallback callback)53 void DesktopMediaListBase::Update(UpdateCallback callback) {
54 DCHECK(sources_.empty());
55 DCHECK(!refresh_callback_);
56 refresh_callback_ = std::move(callback);
57 Refresh(false);
58 }
59
GetSourceCount() const60 int DesktopMediaListBase::GetSourceCount() const {
61 return sources_.size();
62 }
63
GetSource(int index) const64 const DesktopMediaList::Source& DesktopMediaListBase::GetSource(
65 int index) const {
66 DCHECK_GE(index, 0);
67 DCHECK_LT(index, static_cast<int>(sources_.size()));
68 return sources_[index];
69 }
70
GetMediaListType() const71 DesktopMediaID::Type DesktopMediaListBase::GetMediaListType() const {
72 return type_;
73 }
74
SourceDescription(DesktopMediaID id,const base::string16 & name)75 DesktopMediaListBase::SourceDescription::SourceDescription(
76 DesktopMediaID id,
77 const base::string16& name)
78 : id(id), name(name) {}
79
UpdateSourcesList(const std::vector<SourceDescription> & new_sources)80 void DesktopMediaListBase::UpdateSourcesList(
81 const std::vector<SourceDescription>& new_sources) {
82 typedef std::set<DesktopMediaID> SourceSet;
83 SourceSet new_source_set;
84 for (size_t i = 0; i < new_sources.size(); ++i) {
85 new_source_set.insert(new_sources[i].id);
86 }
87 // Iterate through the old sources to find the removed sources.
88 for (size_t i = 0; i < sources_.size(); ++i) {
89 if (new_source_set.find(sources_[i].id) == new_source_set.end()) {
90 sources_.erase(sources_.begin() + i);
91 if (observer_)
92 observer_->OnSourceRemoved(this, i);
93 --i;
94 }
95 }
96 // Iterate through the new sources to find the added sources.
97 if (new_sources.size() > sources_.size()) {
98 SourceSet old_source_set;
99 for (size_t i = 0; i < sources_.size(); ++i) {
100 old_source_set.insert(sources_[i].id);
101 }
102
103 for (size_t i = 0; i < new_sources.size(); ++i) {
104 if (old_source_set.find(new_sources[i].id) == old_source_set.end()) {
105 sources_.insert(sources_.begin() + i, Source());
106 sources_[i].id = new_sources[i].id;
107 sources_[i].name = new_sources[i].name;
108 if (observer_)
109 observer_->OnSourceAdded(this, i);
110 }
111 }
112 }
113 DCHECK_EQ(new_sources.size(), sources_.size());
114
115 // Find the moved/changed sources.
116 size_t pos = 0;
117 while (pos < sources_.size()) {
118 if (!(sources_[pos].id == new_sources[pos].id)) {
119 // Find the source that should be moved to |pos|, starting from |pos + 1|
120 // of |sources_|, because entries before |pos| should have been sorted.
121 size_t old_pos = pos + 1;
122 for (; old_pos < sources_.size(); ++old_pos) {
123 if (sources_[old_pos].id == new_sources[pos].id)
124 break;
125 }
126 DCHECK(sources_[old_pos].id == new_sources[pos].id);
127
128 // Move the source from |old_pos| to |pos|.
129 Source temp = sources_[old_pos];
130 sources_.erase(sources_.begin() + old_pos);
131 sources_.insert(sources_.begin() + pos, temp);
132
133 if (observer_)
134 observer_->OnSourceMoved(this, old_pos, pos);
135 }
136
137 if (sources_[pos].name != new_sources[pos].name) {
138 sources_[pos].name = new_sources[pos].name;
139 if (observer_)
140 observer_->OnSourceNameChanged(this, pos);
141 }
142 ++pos;
143 }
144 }
145
UpdateSourceThumbnail(DesktopMediaID id,const gfx::ImageSkia & image)146 void DesktopMediaListBase::UpdateSourceThumbnail(DesktopMediaID id,
147 const gfx::ImageSkia& image) {
148 // Unlike other methods that check can_refresh(), this one won't cause
149 // OnRefreshComplete() to be called, but the caller is expected to schedule a
150 // call to OnRefreshComplete() after this method has been called as many times
151 // as needed, so the check is still valid.
152 DCHECK(can_refresh());
153
154 for (size_t i = 0; i < sources_.size(); ++i) {
155 if (sources_[i].id == id) {
156 sources_[i].thumbnail = image;
157 if (observer_)
158 observer_->OnSourceThumbnailChanged(this, i);
159 break;
160 }
161 }
162 }
163
164 // static
GetImageHash(const gfx::Image & image)165 uint32_t DesktopMediaListBase::GetImageHash(const gfx::Image& image) {
166 SkBitmap bitmap = image.AsBitmap();
167 return base::FastHash(base::make_span(
168 static_cast<uint8_t*>(bitmap.getPixels()), bitmap.computeByteSize()));
169 }
170
OnRefreshComplete()171 void DesktopMediaListBase::OnRefreshComplete() {
172 DCHECK(refresh_callback_);
173 std::move(refresh_callback_).Run();
174 }
175
ScheduleNextRefresh()176 void DesktopMediaListBase::ScheduleNextRefresh() {
177 DCHECK(!refresh_callback_);
178 refresh_callback_ = base::BindOnce(&DesktopMediaListBase::ScheduleNextRefresh,
179 weak_factory_.GetWeakPtr());
180 content::GetUIThreadTaskRunner({})->PostDelayedTask(
181 FROM_HERE,
182 base::BindOnce(&DesktopMediaListBase::Refresh, weak_factory_.GetWeakPtr(),
183 true),
184 update_period_);
185 }
186