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

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 }
18 task_runner_(base::ThreadTaskRunnerHandle::Get()),
19 weak_factory_(this) {}
20 19
21 RemotingController::~RemotingController() {} 20 RemotingRendererController::~RemotingRendererController() {
21 DCHECK(thread_checker_.CalledOnValidThread());
22 remoting_source_->RemoveClient(this);
23 }
22 24
23 void RemotingController::OnSinkAvailable() { 25 void RemotingRendererController::OnStarted(bool success) {
24 DCHECK(task_runner_->BelongsToCurrentThread()); 26 DCHECK(thread_checker_.CalledOnValidThread());
25 27
26 is_sink_available_ = true; 28 if (success) {
29 VLOG(1) << "Remoting started successively.";
30 if (remote_rendering_started_) {
31 DCHECK(!switch_renderer_cb_.is_null());
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 }
40 }
41
42 void RemotingRendererController::OnSessionStateChanged() {
43 DCHECK(thread_checker_.CalledOnValidThread());
44
45 VLOG(1) << "OnSessionStateChanged: " << remoting_source_->state();
27 UpdateAndMaybeSwitch(); 46 UpdateAndMaybeSwitch();
28 } 47 }
29 48
30 void RemotingController::OnSinkGone() { 49 void RemotingRendererController::OnEnteredFullscreen() {
31 DCHECK(task_runner_->BelongsToCurrentThread()); 50 DCHECK(thread_checker_.CalledOnValidThread());
32
33 is_sink_available_ = false;
34 UpdateAndMaybeSwitch();
35 }
36
37 void RemotingController::OnStarted() {
38 DCHECK(task_runner_->BelongsToCurrentThread());
39
40 VLOG(1) << "Remoting started successively.";
41 if (is_remoting_)
42 switch_renderer_cb_.Run();
43 else
44 remoter_->Stop(mojom::RemotingStopReason::LOCAL_PLAYBACK);
45 }
46
47 void RemotingController::OnStartFailed(mojom::RemotingStartFailReason reason) {
48 DCHECK(task_runner_->BelongsToCurrentThread());
49
50 VLOG(1) << "Failed to start remoting:" << reason;
51 is_remoting_ = false;
52 }
53
54 void RemotingController::OnMessageFromSink(
55 const std::vector<uint8_t>& message) {
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 51
72 is_fullscreen_ = true; 52 is_fullscreen_ = true;
73 UpdateAndMaybeSwitch(); 53 UpdateAndMaybeSwitch();
74 } 54 }
75 55
76 void RemotingController::OnExitedFullscreen() { 56 void RemotingRendererController::OnExitedFullscreen() {
77 DCHECK(task_runner_->BelongsToCurrentThread()); 57 DCHECK(thread_checker_.CalledOnValidThread());
78 58
79 is_fullscreen_ = false; 59 is_fullscreen_ = false;
80 UpdateAndMaybeSwitch(); 60 UpdateAndMaybeSwitch();
81 } 61 }
82 62
83 void RemotingController::OnSetCdm(CdmContext* cdm_context) { 63 void RemotingRendererController::OnSetCdm(CdmContext* cdm_context) {
84 DCHECK(task_runner_->BelongsToCurrentThread()); 64 DCHECK(thread_checker_.CalledOnValidThread());
85 65
86 // TODO(xjz): Not implemented. Will add in up-coming change. 66 auto* cdm = RemotingCdm::From(cdm_context);
87 NOTIMPLEMENTED(); 67 if (!cdm)
68 return;
69
70 remoting_source_->RemoveClient(this);
71 remoting_source_ = cdm->GetRemotingSource();
72 remoting_source_->AddClient(this); // Calls OnSessionStateChanged().
73 UpdateAndMaybeSwitch();
88 } 74 }
89 75
90 void RemotingController::SetSwitchRendererCallback( 76 void RemotingRendererController::SetSwitchRendererCallback(
91 const SwitchRendererCallback& cb) { 77 const base::Closure& cb) {
92 DCHECK(task_runner_->BelongsToCurrentThread()); 78 DCHECK(thread_checker_.CalledOnValidThread());
93 DCHECK(!cb.is_null()); 79 DCHECK(!cb.is_null());
94 80
95 switch_renderer_cb_ = cb; 81 switch_renderer_cb_ = cb;
82 UpdateAndMaybeSwitch();
96 } 83 }
97 84
98 void RemotingController::OnMetadataChanged(const PipelineMetadata& metadata) { 85 void RemotingRendererController::OnMetadataChanged(
99 DCHECK(task_runner_->BelongsToCurrentThread()); 86 const PipelineMetadata& metadata) {
87 DCHECK(thread_checker_.CalledOnValidThread());
100 88
101 has_video_ = metadata.has_video; 89 has_video_ =
102 has_audio_ = metadata.has_audio; 90 metadata.has_video && metadata.video_decoder_config.IsValidConfig();
103 if (!has_video_ && !has_audio_) 91 has_audio_ =
104 return; 92 metadata.has_audio && metadata.audio_decoder_config.IsValidConfig();
105 93
106 // On Android, when using the MediaPlayerRenderer, |has_video_| and 94 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_) { 95 if (has_video_) {
110 video_decoder_config_ = metadata.video_decoder_config; 96 video_decoder_config_ = metadata.video_decoder_config;
111 is_encrypted_ |= video_decoder_config_.is_encrypted(); 97 is_encrypted_ |= video_decoder_config_.is_encrypted();
112 } 98 }
113 if (has_audio_) { 99 if (has_audio_) {
114 audio_decoder_config_ = metadata.audio_decoder_config; 100 audio_decoder_config_ = metadata.audio_decoder_config;
115 is_encrypted_ |= audio_decoder_config_.is_encrypted(); 101 is_encrypted_ |= audio_decoder_config_.is_encrypted();
116 } 102 }
117 UpdateAndMaybeSwitch(); 103 UpdateAndMaybeSwitch();
118 } 104 }
119 105
120 bool RemotingController::IsVideoCodecSupported() { 106 bool RemotingRendererController::IsVideoCodecSupported() {
121 DCHECK(task_runner_->BelongsToCurrentThread()); 107 DCHECK(thread_checker_.CalledOnValidThread());
122 DCHECK(has_video_); 108 DCHECK(has_video_);
123 109
124 switch (video_decoder_config_.codec()) { 110 switch (video_decoder_config_.codec()) {
125 case VideoCodec::kCodecH264: 111 case VideoCodec::kCodecH264:
126 case VideoCodec::kCodecVP8: 112 case VideoCodec::kCodecVP8:
127 return true; 113 return true;
128 default: 114 default:
129 VLOG(2) << "Remoting does not support video codec: " 115 VLOG(2) << "Remoting does not support video codec: "
130 << video_decoder_config_.codec(); 116 << video_decoder_config_.codec();
131 return false; 117 return false;
132 } 118 }
133 } 119 }
134 120
135 bool RemotingController::IsAudioCodecSupported() { 121 bool RemotingRendererController::IsAudioCodecSupported() {
136 DCHECK(task_runner_->BelongsToCurrentThread()); 122 DCHECK(thread_checker_.CalledOnValidThread());
137 DCHECK(has_audio_); 123 DCHECK(has_audio_);
138 124
139 switch (audio_decoder_config_.codec()) { 125 switch (audio_decoder_config_.codec()) {
140 case AudioCodec::kCodecAAC: 126 case AudioCodec::kCodecAAC:
141 case AudioCodec::kCodecMP3: 127 case AudioCodec::kCodecMP3:
142 case AudioCodec::kCodecPCM: 128 case AudioCodec::kCodecPCM:
143 case AudioCodec::kCodecVorbis: 129 case AudioCodec::kCodecVorbis:
144 case AudioCodec::kCodecFLAC: 130 case AudioCodec::kCodecFLAC:
145 case AudioCodec::kCodecAMR_NB: 131 case AudioCodec::kCodecAMR_NB:
146 case AudioCodec::kCodecAMR_WB: 132 case AudioCodec::kCodecAMR_WB:
147 case AudioCodec::kCodecPCM_MULAW: 133 case AudioCodec::kCodecPCM_MULAW:
148 case AudioCodec::kCodecGSM_MS: 134 case AudioCodec::kCodecGSM_MS:
149 case AudioCodec::kCodecPCM_S16BE: 135 case AudioCodec::kCodecPCM_S16BE:
150 case AudioCodec::kCodecPCM_S24BE: 136 case AudioCodec::kCodecPCM_S24BE:
151 case AudioCodec::kCodecOpus: 137 case AudioCodec::kCodecOpus:
152 case AudioCodec::kCodecEAC3: 138 case AudioCodec::kCodecEAC3:
153 case AudioCodec::kCodecPCM_ALAW: 139 case AudioCodec::kCodecPCM_ALAW:
154 case AudioCodec::kCodecALAC: 140 case AudioCodec::kCodecALAC:
155 case AudioCodec::kCodecAC3: 141 case AudioCodec::kCodecAC3:
156 return true; 142 return true;
157 default: 143 default:
158 VLOG(2) << "Remoting does not support audio codec: " 144 VLOG(2) << "Remoting does not support audio codec: "
159 << audio_decoder_config_.codec(); 145 << audio_decoder_config_.codec();
160 return false; 146 return false;
161 } 147 }
162 } 148 }
163 149
164 bool RemotingController::ShouldBeRemoting() { 150 bool RemotingRendererController::ShouldBeRemoting() {
165 DCHECK(task_runner_->BelongsToCurrentThread()); 151 DCHECK(thread_checker_.CalledOnValidThread());
166 152
167 // TODO(xjz): The control logic for EME will be added in a later CL. 153 if (switch_renderer_cb_.is_null())
168 if (is_encrypted_) 154 return false;
155 if (!has_audio_ && !has_video_)
miu 2016/10/31 20:10:31 Does the pipeline ever give us "null" config chang
xjz 2016/11/01 16:45:44 It sounds to me checking this either before or aft
169 return false; 156 return false;
170 157
171 if (!is_sink_available_) 158 // Explicitly ignoring |is_fullscreen_| since all EME content should trigger
172 return false; 159 // the start of remoting immediately, and not later after switching into
173 if (!is_fullscreen_) 160 // fullscreen. This is required by current technical limitations.
174 return false; 161 // Note: For encrypted contents, once the CDM is created remotely, we will let
162 // RemotingRendererImpl handle all failures, e.g., video/audio codec is not
163 // supported, or session is permanently terminated.
164 RemotingSessionState state = remoting_source_->state();
miu 2016/10/31 20:10:31 nit: const
xjz 2016/11/01 16:45:44 Done.
165 if (is_encrypted_) {
166 return state == RemotingSessionState::SESSION_STARTED ||
miu 2016/10/31 20:10:31 Let's make sure and have a comment explaining this
xjz 2016/11/01 16:45:44 Done.
167 state == RemotingSessionState::SESSION_STOPPING ||
168 state == RemotingSessionState::SESSION_PERMANENTLY_STOPPED;
169 }
170
171 switch (state) {
172 case SESSION_CAN_START:
173 case SESSION_STARTING:
174 case SESSION_STARTED:
175 break;
176 case SESSION_STOPPING:
177 case SESSION_UNAVAILABLE:
178 case SESSION_PERMANENTLY_STOPPED:
179 return false;
180 }
175 if (has_video_ && !IsVideoCodecSupported()) 181 if (has_video_ && !IsVideoCodecSupported())
176 return false; 182 return false;
177 if (has_audio_ && !IsAudioCodecSupported()) 183 if (has_audio_ && !IsAudioCodecSupported())
178 return false; 184 return false;
179 return true; 185 return is_fullscreen_;
180 } 186 }
181 187
182 void RemotingController::UpdateAndMaybeSwitch() { 188 void RemotingRendererController::UpdateAndMaybeSwitch() {
183 DCHECK(task_runner_->BelongsToCurrentThread()); 189 DCHECK(thread_checker_.CalledOnValidThread());
184 190
185 // TODO(xjz): The switching logic for encrypted content will be added in a 191 bool should_be_remoting = ShouldBeRemoting();
186 // later CL.
187 192
188 // Demuxer is not initialized yet. 193 if (remote_rendering_started_ == should_be_remoting)
189 if (!has_audio_ && !has_video_)
190 return; 194 return;
191 195
192 DCHECK(!switch_renderer_cb_.is_null()); 196 // Switch between local renderer and remoting renderer.
197 remote_rendering_started_ = should_be_remoting;
193 198
194 bool should_be_remoting = ShouldBeRemoting(); 199 if (remote_rendering_started_) {
195 if (is_remoting_ == should_be_remoting) 200 DCHECK(!switch_renderer_cb_.is_null());
196 return; 201 if (remoting_source_->state() ==
197 202 RemotingSessionState::SESSION_PERMANENTLY_STOPPED) {
198 // Switch between local and remoting. 203 switch_renderer_cb_.Run();
199 is_remoting_ = should_be_remoting; 204 return;
200 if (is_remoting_) { 205 }
201 // |swithc_renderer_cb_.Run()| will be called after remoting is started 206 // |switch_renderer_cb_.Run()| will be called after remoting is started
202 // successfully. 207 // successfully.
203 remoter_->Start(); 208 remoting_source_->StartRemoting(this);
204 } else { 209 } else {
210 // For encrypted contents, it's only valid to switch to remoting renderer,
miu 2016/10/31 20:10:31 s/contents/content/ Oh, and I just noticed we cal
xjz 2016/11/01 16:45:45 Done.
211 // and never back to the local renderer.
212 DCHECK(!is_encrypted_);
205 switch_renderer_cb_.Run(); 213 switch_renderer_cb_.Run();
206 remoter_->Stop(mojom::RemotingStopReason::LOCAL_PLAYBACK); 214 remoting_source_->StopRemoting(this);
207 } 215 }
208 } 216 }
209 217
210 } // namespace media 218 } // namespace media
OLDNEW
« no previous file with comments | « media/remoting/remoting_renderer_controller.h ('k') | media/remoting/remoting_renderer_controller_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698