Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(102)

Side by Side Diff: media/remoting/remoting_renderer_controller.cc

Issue 2457563002: Media Remoting: Add remoting control logic for encrypted contents. (Closed)
Patch Set: Addressed comments. Created 4 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright 2016 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "media/remoting/remoting_controller.h" 5 #include "media/remoting/remoting_renderer_controller.h"
6 6
7 #include "base/bind.h" 7 #include "base/bind.h"
8 #include "base/logging.h" 8 #include "base/logging.h"
9 #include "base/single_thread_task_runner.h" 9 #include "base/threading/thread_checker.h"
10 #include "media/remoting/remoting_cdm.h"
10 11
11 namespace media { 12 namespace media {
12 13
13 RemotingController::RemotingController( 14 RemotingRendererController::RemotingRendererController(
14 mojom::RemotingSourceRequest source_request, 15 scoped_refptr<RemotingSourceImpl> remoting_source)
15 mojom::RemoterPtr remoter) 16 : remoting_source_(remoting_source), weak_factory_(this) {
16 : binding_(this, std::move(source_request)), 17 remoting_source_->AddClient(this);
17 remoter_(std::move(remoter)),
18 task_runner_(base::ThreadTaskRunnerHandle::Get()),
19 weak_factory_(this) {}
20
21 RemotingController::~RemotingController() {}
22
23 void RemotingController::OnSinkAvailable() {
24 DCHECK(task_runner_->BelongsToCurrentThread());
25
26 is_sink_available_ = true;
27 UpdateAndMaybeSwitch();
28 } 18 }
29 19
30 void RemotingController::OnSinkGone() { 20 RemotingRendererController::~RemotingRendererController() {
31 DCHECK(task_runner_->BelongsToCurrentThread()); 21 DCHECK(thread_checker_.CalledOnValidThread());
32 22 remoting_source_->RemoveClient(this);
33 is_sink_available_ = false;
34 UpdateAndMaybeSwitch();
35 } 23 }
36 24
37 void RemotingController::OnStarted() { 25 void RemotingRendererController::OnStarted(bool success) {
38 DCHECK(task_runner_->BelongsToCurrentThread()); 26 DCHECK(thread_checker_.CalledOnValidThread());
39 27
40 VLOG(1) << "Remoting started successively."; 28 if (success) {
41 if (is_remoting_) 29 VLOG(1) << "Remoting started successively.";
42 switch_renderer_cb_.Run(); 30 if (remote_rendering_started_) {
43 else 31 DCHECK(!switch_renderer_cb_.is_null());
44 remoter_->Stop(mojom::RemotingStopReason::LOCAL_PLAYBACK); 32 switch_renderer_cb_.Run();
33 } else {
34 remoting_source_->StopRemoting(this);
35 }
36 } else {
37 VLOG(1) << "Failed to start remoting.";
38 remote_rendering_started_ = false;
39 }
45 } 40 }
46 41
47 void RemotingController::OnStartFailed(mojom::RemotingStartFailReason reason) { 42 void RemotingRendererController::OnSessionStateChanged() {
48 DCHECK(task_runner_->BelongsToCurrentThread()); 43 DCHECK(thread_checker_.CalledOnValidThread());
49 44
50 VLOG(1) << "Failed to start remoting:" << reason; 45 VLOG(1) << "OnSessionStateChanged: " << remoting_source_->state();
51 is_remoting_ = false; 46 bool session_could_remote = session_can_remote_;
47 switch (remoting_source_->state()) {
miu 2016/10/29 02:22:16 Looking at this code again, I think you can simult
xjz 2016/10/31 17:24:00 Done.
48 case SESSION_CAN_START:
49 case SESSION_STARTING:
50 case SESSION_STARTED:
51 session_can_remote_ = true;
52 break;
53 case SESSION_STOPPING:
54 case SESSION_UNAVAILABLE:
55 case SESSION_PERMANENTLY_STOPPED:
56 session_can_remote_ = false;
57 break;
58 }
59 if (session_can_remote_ != session_could_remote)
60 UpdateAndMaybeSwitch();
52 } 61 }
53 62
54 void RemotingController::OnMessageFromSink( 63 void RemotingRendererController::OnEnteredFullscreen() {
55 const std::vector<uint8_t>& message) { 64 DCHECK(thread_checker_.CalledOnValidThread());
56 DCHECK(task_runner_->BelongsToCurrentThread());
57
58 // TODO(xjz): Merge with Eric's CL to handle the RPC messages here.
59 NOTIMPLEMENTED();
60 }
61
62 void RemotingController::OnStopped(mojom::RemotingStopReason reason) {
63 DCHECK(task_runner_->BelongsToCurrentThread());
64
65 VLOG(1) << "Remoting stopped: " << reason;
66 is_remoting_ = false;
67 }
68
69 void RemotingController::OnEnteredFullscreen() {
70 DCHECK(task_runner_->BelongsToCurrentThread());
71 65
72 is_fullscreen_ = true; 66 is_fullscreen_ = true;
73 UpdateAndMaybeSwitch(); 67 UpdateAndMaybeSwitch();
74 } 68 }
75 69
76 void RemotingController::OnExitedFullscreen() { 70 void RemotingRendererController::OnExitedFullscreen() {
77 DCHECK(task_runner_->BelongsToCurrentThread()); 71 DCHECK(thread_checker_.CalledOnValidThread());
78 72
79 is_fullscreen_ = false; 73 is_fullscreen_ = false;
80 UpdateAndMaybeSwitch(); 74 UpdateAndMaybeSwitch();
81 } 75 }
82 76
83 void RemotingController::OnSetCdm(CdmContext* cdm_context) { 77 void RemotingRendererController::OnSetCdm(CdmContext* cdm_context) {
84 DCHECK(task_runner_->BelongsToCurrentThread()); 78 DCHECK(thread_checker_.CalledOnValidThread());
85 79
86 // TODO(xjz): Not implemented. Will add in up-coming change. 80 auto* cdm = RemotingCdm::From(cdm_context);
87 NOTIMPLEMENTED(); 81 if (!cdm)
82 return;
83
84 remoting_source_->RemoveClient(this);
85 remoting_source_ = cdm->GetRemotingSource();
86 remoting_source_->AddClient(this); // Calls OnSessionStateChanged().
87 UpdateAndMaybeSwitch();
88 } 88 }
89 89
90 void RemotingController::SetSwitchRendererCallback( 90 void RemotingRendererController::SetSwitchRendererCallback(
91 const SwitchRendererCallback& cb) { 91 const base::Closure& cb) {
92 DCHECK(task_runner_->BelongsToCurrentThread()); 92 DCHECK(thread_checker_.CalledOnValidThread());
93 DCHECK(!cb.is_null()); 93 DCHECK(!cb.is_null());
94 94
95 switch_renderer_cb_ = cb; 95 switch_renderer_cb_ = cb;
96 UpdateAndMaybeSwitch();
96 } 97 }
97 98
98 void RemotingController::OnMetadataChanged(const PipelineMetadata& metadata) { 99 void RemotingRendererController::OnMetadataChanged(
99 DCHECK(task_runner_->BelongsToCurrentThread()); 100 const PipelineMetadata& metadata) {
101 DCHECK(thread_checker_.CalledOnValidThread());
100 102
101 has_video_ = metadata.has_video; 103 has_video_ =
102 has_audio_ = metadata.has_audio; 104 metadata.has_video && metadata.video_decoder_config.IsValidConfig();
103 if (!has_video_ && !has_audio_) 105 has_audio_ =
104 return; 106 metadata.has_audio && metadata.audio_decoder_config.IsValidConfig();
105 107
106 // On Android, when using the MediaPlayerRenderer, |has_video_| and 108 is_encrypted_ = false;
107 // |has_audio_| will be true, but the respective configs will be empty.
108 // We cannot make any assumptions on the validity of configs.
109 if (has_video_) { 109 if (has_video_) {
110 video_decoder_config_ = metadata.video_decoder_config; 110 video_decoder_config_ = metadata.video_decoder_config;
111 is_encrypted_ |= video_decoder_config_.is_encrypted(); 111 is_encrypted_ |= video_decoder_config_.is_encrypted();
112 } 112 }
113 if (has_audio_) { 113 if (has_audio_) {
114 audio_decoder_config_ = metadata.audio_decoder_config; 114 audio_decoder_config_ = metadata.audio_decoder_config;
115 is_encrypted_ |= audio_decoder_config_.is_encrypted(); 115 is_encrypted_ |= audio_decoder_config_.is_encrypted();
116 } 116 }
117 UpdateAndMaybeSwitch(); 117 UpdateAndMaybeSwitch();
118 } 118 }
119 119
120 bool RemotingController::IsVideoCodecSupported() { 120 bool RemotingRendererController::IsVideoCodecSupported() {
121 DCHECK(task_runner_->BelongsToCurrentThread()); 121 DCHECK(thread_checker_.CalledOnValidThread());
122 DCHECK(has_video_); 122 DCHECK(has_video_);
123 123
124 switch (video_decoder_config_.codec()) { 124 switch (video_decoder_config_.codec()) {
125 case VideoCodec::kCodecH264: 125 case VideoCodec::kCodecH264:
126 case VideoCodec::kCodecVP8: 126 case VideoCodec::kCodecVP8:
127 return true; 127 return true;
128 default: 128 default:
129 VLOG(2) << "Remoting does not support video codec: " 129 VLOG(2) << "Remoting does not support video codec: "
130 << video_decoder_config_.codec(); 130 << video_decoder_config_.codec();
131 return false; 131 return false;
132 } 132 }
133 } 133 }
134 134
135 bool RemotingController::IsAudioCodecSupported() { 135 bool RemotingRendererController::IsAudioCodecSupported() {
136 DCHECK(task_runner_->BelongsToCurrentThread()); 136 DCHECK(thread_checker_.CalledOnValidThread());
137 DCHECK(has_audio_); 137 DCHECK(has_audio_);
138 138
139 switch (audio_decoder_config_.codec()) { 139 switch (audio_decoder_config_.codec()) {
140 case AudioCodec::kCodecAAC: 140 case AudioCodec::kCodecAAC:
141 case AudioCodec::kCodecMP3: 141 case AudioCodec::kCodecMP3:
142 case AudioCodec::kCodecPCM: 142 case AudioCodec::kCodecPCM:
143 case AudioCodec::kCodecVorbis: 143 case AudioCodec::kCodecVorbis:
144 case AudioCodec::kCodecFLAC: 144 case AudioCodec::kCodecFLAC:
145 case AudioCodec::kCodecAMR_NB: 145 case AudioCodec::kCodecAMR_NB:
146 case AudioCodec::kCodecAMR_WB: 146 case AudioCodec::kCodecAMR_WB:
147 case AudioCodec::kCodecPCM_MULAW: 147 case AudioCodec::kCodecPCM_MULAW:
148 case AudioCodec::kCodecGSM_MS: 148 case AudioCodec::kCodecGSM_MS:
149 case AudioCodec::kCodecPCM_S16BE: 149 case AudioCodec::kCodecPCM_S16BE:
150 case AudioCodec::kCodecPCM_S24BE: 150 case AudioCodec::kCodecPCM_S24BE:
151 case AudioCodec::kCodecOpus: 151 case AudioCodec::kCodecOpus:
152 case AudioCodec::kCodecEAC3: 152 case AudioCodec::kCodecEAC3:
153 case AudioCodec::kCodecPCM_ALAW: 153 case AudioCodec::kCodecPCM_ALAW:
154 case AudioCodec::kCodecALAC: 154 case AudioCodec::kCodecALAC:
155 case AudioCodec::kCodecAC3: 155 case AudioCodec::kCodecAC3:
156 return true; 156 return true;
157 default: 157 default:
158 VLOG(2) << "Remoting does not support audio codec: " 158 VLOG(2) << "Remoting does not support audio codec: "
159 << audio_decoder_config_.codec(); 159 << audio_decoder_config_.codec();
160 return false; 160 return false;
161 } 161 }
162 } 162 }
163 163
164 bool RemotingController::ShouldBeRemoting() { 164 bool RemotingRendererController::ShouldBeRemoting() {
165 DCHECK(task_runner_->BelongsToCurrentThread()); 165 DCHECK(thread_checker_.CalledOnValidThread());
166 166
167 // TODO(xjz): The control logic for EME will be added in a later CL. 167 // Explicitly ignoring |is_fullscreen_| since all EME content should trigger
168 if (is_encrypted_) 168 // the start of remoting immediately, and not later after switching into
169 // fullscreen. This is required by current technical limitations.
170 // Note: For encrypted contents, once the CDM is created remotely, we will let
171 // RemotingRendererImpl handle all failures, e.g., video/audio codec is not
172 // supported, or session is permanently terminated.
173 if (is_encrypted_) {
174 return remoting_source_->state() == RemotingSessionState::SESSION_STARTED ||
miu 2016/10/29 02:22:16 Hmm...Things moved around and now I'm confused aga
xjz 2016/10/31 17:24:00 Yes, I should include SESSION_STOPPING here. But t
175 remoting_source_->state() ==
176 RemotingSessionState::SESSION_PERMANENTLY_STOPPED;
177 }
178
179 if (!session_can_remote_)
169 return false; 180 return false;
170 181 if (switch_renderer_cb_.is_null())
171 if (!is_sink_available_)
172 return false; 182 return false;
173 if (!is_fullscreen_) 183 if (!has_audio_ && !has_video_)
174 return false; 184 return false;
175 if (has_video_ && !IsVideoCodecSupported()) 185 if (has_video_ && !IsVideoCodecSupported())
176 return false; 186 return false;
177 if (has_audio_ && !IsAudioCodecSupported()) 187 if (has_audio_ && !IsAudioCodecSupported())
178 return false; 188 return false;
179 return true; 189 return is_fullscreen_;
180 } 190 }
181 191
182 void RemotingController::UpdateAndMaybeSwitch() { 192 void RemotingRendererController::UpdateAndMaybeSwitch() {
183 DCHECK(task_runner_->BelongsToCurrentThread()); 193 DCHECK(thread_checker_.CalledOnValidThread());
184 194
185 // TODO(xjz): The switching logic for encrypted content will be added in a 195 bool should_be_remoting = ShouldBeRemoting();
186 // later CL.
187 196
188 // Demuxer is not initialized yet. 197 if (remote_rendering_started_ == should_be_remoting)
189 if (!has_audio_ && !has_video_)
190 return; 198 return;
191 199
192 DCHECK(!switch_renderer_cb_.is_null()); 200 // Switch between local renderer and remoting renderer.
201 remote_rendering_started_ = should_be_remoting;
193 202
194 bool should_be_remoting = ShouldBeRemoting(); 203 if (remote_rendering_started_) {
195 if (is_remoting_ == should_be_remoting) 204 DCHECK(!switch_renderer_cb_.is_null());
196 return; 205 if (remoting_source_->state() ==
197 206 RemotingSessionState::SESSION_PERMANENTLY_STOPPED) {
198 // Switch between local and remoting. 207 switch_renderer_cb_.Run();
199 is_remoting_ = should_be_remoting; 208 return;
200 if (is_remoting_) { 209 }
201 // |swithc_renderer_cb_.Run()| will be called after remoting is started 210 // |switch_renderer_cb_.Run()| will be called after remoting is started
202 // successfully. 211 // successfully.
203 remoter_->Start(); 212 remoting_source_->StartRemoting(this);
204 } else { 213 } else {
214 DCHECK(!is_encrypted_);
miu 2016/10/29 02:22:16 Please add comment to explain this DCHECK (somethi
xjz 2016/10/31 17:24:00 Done.
205 switch_renderer_cb_.Run(); 215 switch_renderer_cb_.Run();
206 remoter_->Stop(mojom::RemotingStopReason::LOCAL_PLAYBACK); 216 remoting_source_->StopRemoting(this);
207 } 217 }
208 } 218 }
209 219
210 } // namespace media 220 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698