OLD | NEW |
| (Empty) |
1 // Copyright 2013 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 "webkit/renderer/media/android/media_source_delegate.h" | |
6 | |
7 #include "base/message_loop/message_loop_proxy.h" | |
8 #include "base/strings/string_number_conversions.h" | |
9 #include "media/base/android/demuxer_stream_player_params.h" | |
10 #include "media/base/bind_to_loop.h" | |
11 #include "media/base/demuxer_stream.h" | |
12 #include "media/base/media_log.h" | |
13 #include "media/filters/chunk_demuxer.h" | |
14 #include "third_party/WebKit/public/platform/WebString.h" | |
15 #include "third_party/WebKit/public/web/WebMediaSource.h" | |
16 #include "third_party/WebKit/public/web/WebRuntimeFeatures.h" | |
17 #include "webkit/renderer/media/android/webmediaplayer_proxy_android.h" | |
18 #include "webkit/renderer/media/webmediaplayer_util.h" | |
19 #include "webkit/renderer/media/webmediasourceclient_impl.h" | |
20 | |
21 using media::DemuxerStream; | |
22 using media::MediaPlayerHostMsg_DemuxerReady_Params; | |
23 using media::MediaPlayerHostMsg_ReadFromDemuxerAck_Params; | |
24 using WebKit::WebMediaPlayer; | |
25 using WebKit::WebString; | |
26 | |
27 namespace { | |
28 | |
29 // The size of the access unit to transfer in an IPC in case of MediaSource. | |
30 // 16: approximately 250ms of content in 60 fps movies. | |
31 const size_t kAccessUnitSizeForMediaSource = 16; | |
32 | |
33 const uint8 kVorbisPadding[] = { 0xff, 0xff, 0xff, 0xff }; | |
34 | |
35 } // namespace | |
36 | |
37 namespace webkit_media { | |
38 | |
39 #define BIND_TO_RENDER_LOOP(function) \ | |
40 media::BindToLoop(base::MessageLoopProxy::current(), \ | |
41 base::Bind(function, weak_this_.GetWeakPtr())) | |
42 | |
43 #define BIND_TO_RENDER_LOOP_1(function, arg1) \ | |
44 media::BindToLoop(base::MessageLoopProxy::current(), \ | |
45 base::Bind(function, weak_this_.GetWeakPtr(), arg1)) | |
46 | |
47 #define BIND_TO_RENDER_LOOP_2(function, arg1, arg2) \ | |
48 media::BindToLoop(base::MessageLoopProxy::current(), \ | |
49 base::Bind(function, weak_this_.GetWeakPtr(), arg1, arg2)) | |
50 | |
51 #define BIND_TO_RENDER_LOOP_3(function, arg1, arg2, arg3) \ | |
52 media::BindToLoop(base::MessageLoopProxy::current(), \ | |
53 base::Bind(function, \ | |
54 weak_this_.GetWeakPtr(), arg1, arg2, arg3)) | |
55 | |
56 static void LogMediaSourceError(const scoped_refptr<media::MediaLog>& media_log, | |
57 const std::string& error) { | |
58 media_log->AddEvent(media_log->CreateMediaSourceErrorEvent(error)); | |
59 } | |
60 | |
61 MediaSourceDelegate::MediaSourceDelegate(WebMediaPlayerProxyAndroid* proxy, | |
62 int player_id, | |
63 media::MediaLog* media_log) | |
64 : weak_this_(this), | |
65 proxy_(proxy), | |
66 player_id_(player_id), | |
67 media_log_(media_log), | |
68 demuxer_(NULL), | |
69 audio_params_(new MediaPlayerHostMsg_ReadFromDemuxerAck_Params), | |
70 video_params_(new MediaPlayerHostMsg_ReadFromDemuxerAck_Params), | |
71 seeking_(false), | |
72 key_added_(false), | |
73 access_unit_size_(0) { | |
74 } | |
75 | |
76 MediaSourceDelegate::~MediaSourceDelegate() { | |
77 DVLOG(1) << "MediaSourceDelegate::~MediaSourceDelegate() : " << player_id_; | |
78 DCHECK(!chunk_demuxer_); | |
79 DCHECK(!demuxer_); | |
80 } | |
81 | |
82 void MediaSourceDelegate::Destroy() { | |
83 DVLOG(1) << "MediaSourceDelegate::Destroy() : " << player_id_; | |
84 if (!demuxer_) { | |
85 delete this; | |
86 return; | |
87 } | |
88 | |
89 duration_change_cb_.Reset(); | |
90 update_network_state_cb_.Reset(); | |
91 media_source_.reset(); | |
92 proxy_ = NULL; | |
93 | |
94 demuxer_ = NULL; | |
95 if (chunk_demuxer_) | |
96 chunk_demuxer_->Stop( | |
97 BIND_TO_RENDER_LOOP(&MediaSourceDelegate::OnDemuxerStopDone)); | |
98 } | |
99 | |
100 void MediaSourceDelegate::InitializeMediaSource( | |
101 WebKit::WebMediaSource* media_source, | |
102 const media::NeedKeyCB& need_key_cb, | |
103 const UpdateNetworkStateCB& update_network_state_cb, | |
104 const DurationChangeCB& duration_change_cb) { | |
105 DCHECK(media_source); | |
106 media_source_.reset(media_source); | |
107 need_key_cb_ = need_key_cb; | |
108 update_network_state_cb_ = update_network_state_cb; | |
109 duration_change_cb_ = duration_change_cb; | |
110 | |
111 chunk_demuxer_.reset(new media::ChunkDemuxer( | |
112 BIND_TO_RENDER_LOOP(&MediaSourceDelegate::OnDemuxerOpened), | |
113 BIND_TO_RENDER_LOOP_1(&MediaSourceDelegate::OnNeedKey, ""), | |
114 base::Bind(&MediaSourceDelegate::OnAddTextTrack, | |
115 base::Unretained(this)), | |
116 base::Bind(&LogMediaSourceError, media_log_))); | |
117 chunk_demuxer_->Initialize(this, | |
118 BIND_TO_RENDER_LOOP(&MediaSourceDelegate::OnDemuxerInitDone)); | |
119 demuxer_ = chunk_demuxer_.get(); | |
120 access_unit_size_ = kAccessUnitSizeForMediaSource; | |
121 } | |
122 | |
123 #if defined(GOOGLE_TV) | |
124 void MediaSourceDelegate::InitializeMediaStream( | |
125 media::Demuxer* demuxer, | |
126 const UpdateNetworkStateCB& update_network_state_cb) { | |
127 DCHECK(demuxer); | |
128 demuxer_ = demuxer; | |
129 update_network_state_cb_ = update_network_state_cb; | |
130 | |
131 demuxer_->Initialize(this, | |
132 BIND_TO_RENDER_LOOP(&MediaSourceDelegate::OnDemuxerInitDone)); | |
133 // When playing Media Stream, don't wait to accumulate multiple packets per | |
134 // IPC communication. | |
135 access_unit_size_ = 1; | |
136 } | |
137 #endif | |
138 | |
139 const WebKit::WebTimeRanges& MediaSourceDelegate::Buffered() { | |
140 buffered_web_time_ranges_ = | |
141 ConvertToWebTimeRanges(buffered_time_ranges_); | |
142 return buffered_web_time_ranges_; | |
143 } | |
144 | |
145 size_t MediaSourceDelegate::DecodedFrameCount() const { | |
146 return statistics_.video_frames_decoded; | |
147 } | |
148 | |
149 size_t MediaSourceDelegate::DroppedFrameCount() const { | |
150 return statistics_.video_frames_dropped; | |
151 } | |
152 | |
153 size_t MediaSourceDelegate::AudioDecodedByteCount() const { | |
154 return statistics_.audio_bytes_decoded; | |
155 } | |
156 | |
157 size_t MediaSourceDelegate::VideoDecodedByteCount() const { | |
158 return statistics_.video_bytes_decoded; | |
159 } | |
160 | |
161 void MediaSourceDelegate::Seek(base::TimeDelta time) { | |
162 DVLOG(1) << "MediaSourceDelegate::Seek(" << time.InSecondsF() << ") : " | |
163 << player_id_; | |
164 seeking_ = true; | |
165 DCHECK(demuxer_); | |
166 if (chunk_demuxer_) | |
167 chunk_demuxer_->StartWaitingForSeek(); | |
168 demuxer_->Seek(time, | |
169 BIND_TO_RENDER_LOOP(&MediaSourceDelegate::OnDemuxerError)); | |
170 } | |
171 | |
172 void MediaSourceDelegate::CancelPendingSeek() { | |
173 if (chunk_demuxer_) | |
174 chunk_demuxer_->CancelPendingSeek(); | |
175 } | |
176 | |
177 void MediaSourceDelegate::SetTotalBytes(int64 total_bytes) { | |
178 NOTIMPLEMENTED(); | |
179 } | |
180 | |
181 void MediaSourceDelegate::AddBufferedByteRange(int64 start, int64 end) { | |
182 NOTIMPLEMENTED(); | |
183 } | |
184 | |
185 void MediaSourceDelegate::AddBufferedTimeRange(base::TimeDelta start, | |
186 base::TimeDelta end) { | |
187 buffered_time_ranges_.Add(start, end); | |
188 } | |
189 | |
190 void MediaSourceDelegate::SetDuration(base::TimeDelta duration) { | |
191 DVLOG(1) << "MediaSourceDelegate::SetDuration(" << duration.InSecondsF() | |
192 << ") : " << player_id_; | |
193 // Notify our owner (e.g. WebMediaPlayerAndroid) that | |
194 // duration has changed. | |
195 if (!duration_change_cb_.is_null()) | |
196 duration_change_cb_.Run(duration); | |
197 } | |
198 | |
199 void MediaSourceDelegate::OnReadFromDemuxer(media::DemuxerStream::Type type, | |
200 bool seek_done) { | |
201 DVLOG(1) << "MediaSourceDelegate::OnReadFromDemuxer(" << type | |
202 << ", " << seek_done << ") : " << player_id_; | |
203 if (seeking_ && !seek_done) | |
204 return; // Drop the request during seeking. | |
205 seeking_ = false; | |
206 | |
207 DCHECK(type == DemuxerStream::AUDIO || type == DemuxerStream::VIDEO); | |
208 // The access unit size should have been initialized properly at this stage. | |
209 DCHECK_GT(access_unit_size_, 0u); | |
210 MediaPlayerHostMsg_ReadFromDemuxerAck_Params* params = | |
211 type == DemuxerStream::AUDIO ? audio_params_.get() : video_params_.get(); | |
212 params->type = type; | |
213 params->access_units.resize(access_unit_size_); | |
214 DemuxerStream* stream = demuxer_->GetStream(type); | |
215 DCHECK(stream != NULL); | |
216 ReadFromDemuxerStream(stream, params, 0); | |
217 } | |
218 | |
219 void MediaSourceDelegate::ReadFromDemuxerStream( | |
220 DemuxerStream* stream, | |
221 MediaPlayerHostMsg_ReadFromDemuxerAck_Params* params, | |
222 size_t index) { | |
223 stream->Read(BIND_TO_RENDER_LOOP_3(&MediaSourceDelegate::OnBufferReady, | |
224 stream, params, index)); | |
225 } | |
226 | |
227 void MediaSourceDelegate::OnBufferReady( | |
228 DemuxerStream* stream, | |
229 MediaPlayerHostMsg_ReadFromDemuxerAck_Params* params, | |
230 size_t index, | |
231 DemuxerStream::Status status, | |
232 const scoped_refptr<media::DecoderBuffer>& buffer) { | |
233 DVLOG(1) << "MediaSourceDelegate::OnBufferReady() : " << player_id_; | |
234 DCHECK(status == DemuxerStream::kAborted || | |
235 index < params->access_units.size()); | |
236 bool is_audio = stream->type() == DemuxerStream::AUDIO; | |
237 if (status != DemuxerStream::kAborted && | |
238 index >= params->access_units.size()) { | |
239 LOG(ERROR) << "The internal state inconsistency onBufferReady: " | |
240 << (is_audio ? "Audio" : "Video") << ", index " << index | |
241 <<", size " << params->access_units.size() | |
242 << ", status " << static_cast<int>(status); | |
243 return; | |
244 } | |
245 switch (status) { | |
246 case DemuxerStream::kAborted: | |
247 // Because the abort was caused by the seek, don't respond ack. | |
248 return; | |
249 | |
250 case DemuxerStream::kConfigChanged: | |
251 // In case of kConfigChanged, need to read decoder_config once | |
252 // for the next reads. | |
253 if (is_audio) { | |
254 stream->audio_decoder_config(); | |
255 } else { | |
256 gfx::Size size = stream->video_decoder_config().coded_size(); | |
257 DVLOG(1) << "Video config is changed: " << | |
258 size.width() << "x" << size.height(); | |
259 } | |
260 params->access_units[index].status = status; | |
261 params->access_units.resize(index + 1); | |
262 break; | |
263 | |
264 case DemuxerStream::kOk: | |
265 params->access_units[index].status = status; | |
266 if (buffer->IsEndOfStream()) { | |
267 params->access_units[index].end_of_stream = true; | |
268 params->access_units.resize(index + 1); | |
269 break; | |
270 } | |
271 // TODO(ycheo): We assume that the inputed stream will be decoded | |
272 // right away. | |
273 // Need to implement this properly using MediaPlayer.OnInfoListener. | |
274 if (is_audio) { | |
275 statistics_.audio_bytes_decoded += buffer->GetDataSize(); | |
276 } else { | |
277 statistics_.video_bytes_decoded += buffer->GetDataSize(); | |
278 statistics_.video_frames_decoded++; | |
279 } | |
280 params->access_units[index].timestamp = buffer->GetTimestamp(); | |
281 params->access_units[index].data = std::vector<uint8>( | |
282 buffer->GetData(), | |
283 buffer->GetData() + buffer->GetDataSize()); | |
284 #if !defined(GOOGLE_TV) | |
285 // Vorbis needs 4 extra bytes padding on Android. Check | |
286 // NuMediaExtractor.cpp in Android source code. | |
287 if (is_audio && media::kCodecVorbis == | |
288 stream->audio_decoder_config().codec()) { | |
289 params->access_units[index].data.insert( | |
290 params->access_units[index].data.end(), kVorbisPadding, | |
291 kVorbisPadding + 4); | |
292 } | |
293 #endif | |
294 if (buffer->GetDecryptConfig()) { | |
295 params->access_units[index].key_id = std::vector<char>( | |
296 buffer->GetDecryptConfig()->key_id().begin(), | |
297 buffer->GetDecryptConfig()->key_id().end()); | |
298 params->access_units[index].iv = std::vector<char>( | |
299 buffer->GetDecryptConfig()->iv().begin(), | |
300 buffer->GetDecryptConfig()->iv().end()); | |
301 params->access_units[index].subsamples = | |
302 buffer->GetDecryptConfig()->subsamples(); | |
303 } | |
304 if (++index < params->access_units.size()) { | |
305 ReadFromDemuxerStream(stream, params, index); | |
306 return; | |
307 } | |
308 break; | |
309 | |
310 default: | |
311 NOTREACHED(); | |
312 } | |
313 | |
314 if (proxy_) | |
315 proxy_->ReadFromDemuxerAck(player_id_, *params); | |
316 params->access_units.resize(0); | |
317 } | |
318 | |
319 void MediaSourceDelegate::OnDemuxerError( | |
320 media::PipelineStatus status) { | |
321 DVLOG(1) << "MediaSourceDelegate::OnDemuxerError(" << status << ") : " | |
322 << player_id_; | |
323 if (status != media::PIPELINE_OK && !update_network_state_cb_.is_null()) | |
324 update_network_state_cb_.Run(PipelineErrorToNetworkState(status)); | |
325 } | |
326 | |
327 void MediaSourceDelegate::OnDemuxerInitDone( | |
328 media::PipelineStatus status) { | |
329 DVLOG(1) << "MediaSourceDelegate::OnDemuxerInitDone(" << status << ") : " | |
330 << player_id_; | |
331 if (status != media::PIPELINE_OK) { | |
332 OnDemuxerError(status); | |
333 return; | |
334 } | |
335 if (CanNotifyDemuxerReady()) | |
336 NotifyDemuxerReady(""); | |
337 } | |
338 | |
339 void MediaSourceDelegate::OnDemuxerStopDone() { | |
340 DVLOG(1) << "MediaSourceDelegate::OnDemuxerStopDone() : " << player_id_; | |
341 chunk_demuxer_.reset(); | |
342 delete this; | |
343 } | |
344 | |
345 void MediaSourceDelegate::OnMediaConfigRequest() { | |
346 if (CanNotifyDemuxerReady()) | |
347 NotifyDemuxerReady(""); | |
348 } | |
349 | |
350 void MediaSourceDelegate::NotifyKeyAdded(const std::string& key_system) { | |
351 // TODO(kjyoun): Enhance logic to detect when to call NotifyDemuxerReady() | |
352 // For now, we calls it when the first key is added. | |
353 if (key_added_) | |
354 return; | |
355 key_added_ = true; | |
356 if (CanNotifyDemuxerReady()) | |
357 NotifyDemuxerReady(key_system); | |
358 } | |
359 | |
360 bool MediaSourceDelegate::CanNotifyDemuxerReady() { | |
361 if (key_added_) | |
362 return true; | |
363 DemuxerStream* audio_stream = demuxer_->GetStream(DemuxerStream::AUDIO); | |
364 if (audio_stream && audio_stream->audio_decoder_config().is_encrypted()) | |
365 return false; | |
366 DemuxerStream* video_stream = demuxer_->GetStream(DemuxerStream::VIDEO); | |
367 if (video_stream && video_stream->video_decoder_config().is_encrypted()) | |
368 return false; | |
369 return true; | |
370 } | |
371 | |
372 void MediaSourceDelegate::NotifyDemuxerReady(const std::string& key_system) { | |
373 DCHECK(demuxer_); | |
374 MediaPlayerHostMsg_DemuxerReady_Params params; | |
375 DemuxerStream* audio_stream = demuxer_->GetStream(DemuxerStream::AUDIO); | |
376 if (audio_stream) { | |
377 const media::AudioDecoderConfig& config = | |
378 audio_stream->audio_decoder_config(); | |
379 params.audio_codec = config.codec(); | |
380 params.audio_channels = | |
381 media::ChannelLayoutToChannelCount(config.channel_layout()); | |
382 params.audio_sampling_rate = config.samples_per_second(); | |
383 params.is_audio_encrypted = config.is_encrypted(); | |
384 params.audio_extra_data = std::vector<uint8>( | |
385 config.extra_data(), config.extra_data() + config.extra_data_size()); | |
386 } | |
387 DemuxerStream* video_stream = demuxer_->GetStream(DemuxerStream::VIDEO); | |
388 if (video_stream) { | |
389 const media::VideoDecoderConfig& config = | |
390 video_stream->video_decoder_config(); | |
391 params.video_codec = config.codec(); | |
392 params.video_size = config.natural_size(); | |
393 params.is_video_encrypted = config.is_encrypted(); | |
394 params.video_extra_data = std::vector<uint8>( | |
395 config.extra_data(), config.extra_data() + config.extra_data_size()); | |
396 } | |
397 params.duration_ms = GetDurationMs(); | |
398 params.key_system = key_system; | |
399 | |
400 if (proxy_) | |
401 proxy_->DemuxerReady(player_id_, params); | |
402 } | |
403 | |
404 int MediaSourceDelegate::GetDurationMs() { | |
405 if (!chunk_demuxer_) | |
406 return -1; | |
407 | |
408 double duration_ms = chunk_demuxer_->GetDuration() * 1000; | |
409 if (duration_ms > std::numeric_limits<int32>::max()) { | |
410 LOG(WARNING) << "Duration from ChunkDemuxer is too large; probably " | |
411 "something has gone wrong."; | |
412 return std::numeric_limits<int32>::max(); | |
413 } | |
414 return duration_ms; | |
415 } | |
416 | |
417 void MediaSourceDelegate::OnDemuxerOpened() { | |
418 if (!media_source_) | |
419 return; | |
420 | |
421 media_source_->open(new WebMediaSourceClientImpl( | |
422 chunk_demuxer_.get(), base::Bind(&LogMediaSourceError, media_log_))); | |
423 } | |
424 | |
425 void MediaSourceDelegate::OnNeedKey(const std::string& session_id, | |
426 const std::string& type, | |
427 scoped_ptr<uint8[]> init_data, | |
428 int init_data_size) { | |
429 if (need_key_cb_.is_null()) | |
430 return; | |
431 | |
432 need_key_cb_.Run(session_id, type, init_data.Pass(), init_data_size); | |
433 } | |
434 | |
435 scoped_ptr<media::TextTrack> MediaSourceDelegate::OnAddTextTrack( | |
436 media::TextKind kind, | |
437 const std::string& label, | |
438 const std::string& language) { | |
439 return scoped_ptr<media::TextTrack>(); | |
440 } | |
441 | |
442 } // namespace webkit_media | |
OLD | NEW |