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