OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 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/base/pipeline.h" | 5 #include "media/base/pipeline.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 | 8 |
9 #include "base/bind.h" | 9 #include "base/bind.h" |
10 #include "base/callback.h" | 10 #include "base/callback.h" |
(...skipping 203 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
214 } | 214 } |
215 } | 215 } |
216 | 216 |
217 base::TimeDelta Pipeline::GetCurrentTime() const { | 217 base::TimeDelta Pipeline::GetCurrentTime() const { |
218 base::AutoLock auto_lock(lock_); | 218 base::AutoLock auto_lock(lock_); |
219 return GetCurrentTime_Locked(); | 219 return GetCurrentTime_Locked(); |
220 } | 220 } |
221 | 221 |
222 base::TimeDelta Pipeline::GetCurrentTime_Locked() const { | 222 base::TimeDelta Pipeline::GetCurrentTime_Locked() const { |
223 lock_.AssertAcquired(); | 223 lock_.AssertAcquired(); |
224 base::TimeDelta elapsed = clock_->Elapsed(); | 224 return clock_->Elapsed(); |
225 if (elapsed > duration_) | |
226 return duration_; | |
227 | |
228 return elapsed; | |
229 } | 225 } |
230 | 226 |
231 base::TimeDelta Pipeline::GetBufferedTime() { | 227 base::TimeDelta Pipeline::GetBufferedTime() { |
232 base::AutoLock auto_lock(lock_); | 228 base::AutoLock auto_lock(lock_); |
233 | 229 |
234 // If media is fully loaded, then return duration. | 230 // If media is fully loaded, then return duration. |
235 if (local_source_ || total_bytes_ == buffered_bytes_) { | 231 if (local_source_ || total_bytes_ == buffered_bytes_) { |
236 max_buffered_time_ = duration_; | 232 max_buffered_time_ = clock_->Duration(); |
237 return duration_; | 233 return max_buffered_time_; |
238 } | 234 } |
239 | 235 |
240 base::TimeDelta current_time = GetCurrentTime_Locked(); | 236 base::TimeDelta current_time = GetCurrentTime_Locked(); |
241 | 237 |
242 // If buffered time was set, we report that value directly. | 238 // If buffered time was set, we report that value directly. |
243 if (buffered_time_.ToInternalValue() > 0) | 239 if (buffered_time_.ToInternalValue() > 0) |
244 return std::max(buffered_time_, current_time); | 240 return std::max(buffered_time_, current_time); |
245 | 241 |
246 if (total_bytes_ == 0) | 242 if (total_bytes_ == 0) |
247 return base::TimeDelta(); | 243 return base::TimeDelta(); |
248 | 244 |
249 // If buffered time was not set, we use current time, current bytes, and | 245 // If buffered time was not set, we use current time, current bytes, and |
250 // buffered bytes to estimate the buffered time. | 246 // buffered bytes to estimate the buffered time. |
251 double estimated_rate = duration_.InMillisecondsF() / total_bytes_; | 247 double estimated_rate = |
| 248 clock_->Duration().InMillisecondsF() / total_bytes_; |
252 double estimated_current_time = estimated_rate * current_bytes_; | 249 double estimated_current_time = estimated_rate * current_bytes_; |
253 DCHECK_GE(buffered_bytes_, current_bytes_); | 250 DCHECK_GE(buffered_bytes_, current_bytes_); |
254 base::TimeDelta buffered_time = base::TimeDelta::FromMilliseconds( | 251 base::TimeDelta buffered_time = base::TimeDelta::FromMilliseconds( |
255 static_cast<int64>(estimated_rate * (buffered_bytes_ - current_bytes_) + | 252 static_cast<int64>(estimated_rate * (buffered_bytes_ - current_bytes_) + |
256 estimated_current_time)); | 253 estimated_current_time)); |
257 | 254 |
258 // Cap approximated buffered time at the length of the video. | 255 // Cap approximated buffered time at the length of the video. |
259 buffered_time = std::min(buffered_time, duration_); | 256 buffered_time = std::min(buffered_time, clock_->Duration()); |
260 | 257 |
261 // Make sure buffered_time is at least the current time | 258 // Make sure buffered_time is at least the current time |
262 buffered_time = std::max(buffered_time, current_time); | 259 buffered_time = std::max(buffered_time, current_time); |
263 | 260 |
264 // Only print the max buffered time for smooth buffering. | 261 // Only print the max buffered time for smooth buffering. |
265 max_buffered_time_ = std::max(buffered_time, max_buffered_time_); | 262 max_buffered_time_ = std::max(buffered_time, max_buffered_time_); |
266 | 263 |
267 return max_buffered_time_; | 264 return max_buffered_time_; |
268 } | 265 } |
269 | 266 |
270 base::TimeDelta Pipeline::GetMediaDuration() const { | 267 base::TimeDelta Pipeline::GetMediaDuration() const { |
271 base::AutoLock auto_lock(lock_); | 268 base::AutoLock auto_lock(lock_); |
272 return duration_; | 269 return clock_->Duration(); |
273 } | 270 } |
274 | 271 |
275 int64 Pipeline::GetBufferedBytes() const { | 272 int64 Pipeline::GetBufferedBytes() const { |
276 base::AutoLock auto_lock(lock_); | 273 base::AutoLock auto_lock(lock_); |
277 return buffered_bytes_; | 274 return buffered_bytes_; |
278 } | 275 } |
279 | 276 |
280 int64 Pipeline::GetTotalBytes() const { | 277 int64 Pipeline::GetTotalBytes() const { |
281 base::AutoLock auto_lock(lock_); | 278 base::AutoLock auto_lock(lock_); |
282 return total_bytes_; | 279 return total_bytes_; |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
323 | 320 |
324 void Pipeline::ResetState() { | 321 void Pipeline::ResetState() { |
325 base::AutoLock auto_lock(lock_); | 322 base::AutoLock auto_lock(lock_); |
326 const base::TimeDelta kZero; | 323 const base::TimeDelta kZero; |
327 running_ = false; | 324 running_ = false; |
328 stop_pending_ = false; | 325 stop_pending_ = false; |
329 seek_pending_ = false; | 326 seek_pending_ = false; |
330 tearing_down_ = false; | 327 tearing_down_ = false; |
331 error_caused_teardown_ = false; | 328 error_caused_teardown_ = false; |
332 playback_rate_change_pending_ = false; | 329 playback_rate_change_pending_ = false; |
333 duration_ = kZero; | |
334 buffered_time_ = kZero; | 330 buffered_time_ = kZero; |
335 buffered_bytes_ = 0; | 331 buffered_bytes_ = 0; |
336 streaming_ = false; | 332 streaming_ = false; |
337 local_source_ = false; | 333 local_source_ = false; |
338 total_bytes_ = 0; | 334 total_bytes_ = 0; |
339 natural_size_.SetSize(0, 0); | 335 natural_size_.SetSize(0, 0); |
340 volume_ = 1.0f; | 336 volume_ = 1.0f; |
341 preload_ = AUTO; | 337 preload_ = AUTO; |
342 playback_rate_ = 0.0f; | 338 playback_rate_ = 0.0f; |
343 pending_playback_rate_ = 0.0f; | 339 pending_playback_rate_ = 0.0f; |
344 status_ = PIPELINE_OK; | 340 status_ = PIPELINE_OK; |
345 has_audio_ = false; | 341 has_audio_ = false; |
346 has_video_ = false; | 342 has_video_ = false; |
347 waiting_for_clock_update_ = false; | 343 waiting_for_clock_update_ = false; |
348 audio_disabled_ = false; | 344 audio_disabled_ = false; |
349 clock_->SetTime(kZero); | 345 clock_->Reset(); |
350 download_rate_monitor_.Reset(); | 346 download_rate_monitor_.Reset(); |
351 } | 347 } |
352 | 348 |
353 void Pipeline::SetState(State next_state) { | 349 void Pipeline::SetState(State next_state) { |
354 if (state_ != kStarted && next_state == kStarted && | 350 if (state_ != kStarted && next_state == kStarted && |
355 !creation_time_.is_null()) { | 351 !creation_time_.is_null()) { |
356 UMA_HISTOGRAM_TIMES( | 352 UMA_HISTOGRAM_TIMES( |
357 "Media.TimeToPipelineStarted", base::Time::Now() - creation_time_); | 353 "Media.TimeToPipelineStarted", base::Time::Now() - creation_time_); |
358 creation_time_ = base::Time(); | 354 creation_time_ = base::Time(); |
359 } | 355 } |
(...skipping 91 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
451 base::TimeDelta Pipeline::GetTime() const { | 447 base::TimeDelta Pipeline::GetTime() const { |
452 DCHECK(IsRunning()); | 448 DCHECK(IsRunning()); |
453 return GetCurrentTime(); | 449 return GetCurrentTime(); |
454 } | 450 } |
455 | 451 |
456 base::TimeDelta Pipeline::GetDuration() const { | 452 base::TimeDelta Pipeline::GetDuration() const { |
457 DCHECK(IsRunning()); | 453 DCHECK(IsRunning()); |
458 return GetMediaDuration(); | 454 return GetMediaDuration(); |
459 } | 455 } |
460 | 456 |
461 void Pipeline::SetTime(base::TimeDelta time) { | 457 void Pipeline::OnAudioTimeUpdate(base::TimeDelta time, |
| 458 base::TimeDelta max_time) { |
| 459 DCHECK(time <= max_time); |
462 DCHECK(IsRunning()); | 460 DCHECK(IsRunning()); |
463 base::AutoLock auto_lock(lock_); | 461 base::AutoLock auto_lock(lock_); |
464 | 462 |
465 // If we were waiting for a valid timestamp and such timestamp arrives, we | 463 if (!has_audio_) |
466 // need to clear the flag for waiting and start the clock. | |
467 if (waiting_for_clock_update_) { | |
468 if (time < clock_->Elapsed()) | |
469 return; | |
470 clock_->SetTime(time); | |
471 StartClockIfWaitingForTimeUpdate_Locked(); | |
472 return; | 464 return; |
473 } | 465 if (waiting_for_clock_update_ && time < clock_->Elapsed()) |
474 clock_->SetTime(time); | 466 return; |
| 467 |
| 468 clock_->SetTime(time, max_time); |
| 469 StartClockIfWaitingForTimeUpdate_Locked(); |
| 470 } |
| 471 |
| 472 void Pipeline::OnVideoTimeUpdate(base::TimeDelta max_time) { |
| 473 DCHECK(IsRunning()); |
| 474 base::AutoLock auto_lock(lock_); |
| 475 |
| 476 if (has_audio_) |
| 477 return; |
| 478 |
| 479 DCHECK(!waiting_for_clock_update_); |
| 480 DCHECK(clock_->Elapsed() <= max_time); |
| 481 clock_->SetMaxTime(max_time); |
475 } | 482 } |
476 | 483 |
477 void Pipeline::SetDuration(base::TimeDelta duration) { | 484 void Pipeline::SetDuration(base::TimeDelta duration) { |
478 DCHECK(IsRunning()); | 485 DCHECK(IsRunning()); |
479 media_log_->AddEvent( | 486 media_log_->AddEvent( |
480 media_log_->CreateTimeEvent( | 487 media_log_->CreateTimeEvent( |
481 MediaLogEvent::DURATION_SET, "duration", duration)); | 488 MediaLogEvent::DURATION_SET, "duration", duration)); |
482 UMA_HISTOGRAM_LONG_TIMES("Media.Duration", duration); | 489 UMA_HISTOGRAM_LONG_TIMES("Media.Duration", duration); |
483 | 490 |
484 base::AutoLock auto_lock(lock_); | 491 base::AutoLock auto_lock(lock_); |
485 duration_ = duration; | 492 clock_->SetDuration(duration); |
486 } | 493 } |
487 | 494 |
488 void Pipeline::SetBufferedTime(base::TimeDelta buffered_time) { | 495 void Pipeline::SetBufferedTime(base::TimeDelta buffered_time) { |
489 DCHECK(IsRunning()); | 496 DCHECK(IsRunning()); |
490 base::AutoLock auto_lock(lock_); | 497 base::AutoLock auto_lock(lock_); |
491 buffered_time_ = buffered_time; | 498 buffered_time_ = buffered_time; |
492 } | 499 } |
493 | 500 |
494 void Pipeline::SetTotalBytes(int64 total_bytes) { | 501 void Pipeline::SetTotalBytes(int64 total_bytes) { |
495 DCHECK(IsRunning()); | 502 DCHECK(IsRunning()); |
(...skipping 412 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
908 } | 915 } |
909 | 916 |
910 if (video_renderer_ && !video_renderer_->HasEnded()) { | 917 if (video_renderer_ && !video_renderer_->HasEnded()) { |
911 return; | 918 return; |
912 } | 919 } |
913 | 920 |
914 // Transition to ended, executing the callback if present. | 921 // Transition to ended, executing the callback if present. |
915 SetState(kEnded); | 922 SetState(kEnded); |
916 { | 923 { |
917 base::AutoLock auto_lock(lock_); | 924 base::AutoLock auto_lock(lock_); |
918 clock_->Pause(); | 925 clock_->EndOfStream(); |
919 clock_->SetTime(duration_); | |
920 } | 926 } |
921 | 927 |
922 if (!ended_callback_.is_null()) { | 928 if (!ended_callback_.is_null()) { |
923 ended_callback_.Run(status_); | 929 ended_callback_.Run(status_); |
924 } | 930 } |
925 } | 931 } |
926 | 932 |
927 void Pipeline::NotifyNetworkEventTask(NetworkEvent type) { | 933 void Pipeline::NotifyNetworkEventTask(NetworkEvent type) { |
928 DCHECK_EQ(MessageLoop::current(), message_loop_); | 934 DCHECK_EQ(MessageLoop::current(), message_loop_); |
929 if (!network_callback_.is_null()) | 935 if (!network_callback_.is_null()) |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
969 NOTREACHED() << "Invalid current state: " << state_; | 975 NOTREACHED() << "Invalid current state: " << state_; |
970 SetError(PIPELINE_ERROR_ABORT); | 976 SetError(PIPELINE_ERROR_ABORT); |
971 return; | 977 return; |
972 } | 978 } |
973 | 979 |
974 // Decrement the number of remaining transitions, making sure to transition | 980 // Decrement the number of remaining transitions, making sure to transition |
975 // to the next state if needed. | 981 // to the next state if needed. |
976 SetState(FindNextState(state_)); | 982 SetState(FindNextState(state_)); |
977 if (state_ == kSeeking) { | 983 if (state_ == kSeeking) { |
978 base::AutoLock auto_lock(lock_); | 984 base::AutoLock auto_lock(lock_); |
979 clock_->SetTime(seek_timestamp_); | 985 clock_->SetTime(seek_timestamp_, seek_timestamp_); |
980 } | 986 } |
981 | 987 |
982 // Carry out the action for the current state. | 988 // Carry out the action for the current state. |
983 if (TransientState(state_)) { | 989 if (TransientState(state_)) { |
984 if (state_ == kPausing) { | 990 if (state_ == kPausing) { |
985 pipeline_filter_->Pause( | 991 pipeline_filter_->Pause( |
986 base::Bind(&Pipeline::OnFilterStateTransition, this)); | 992 base::Bind(&Pipeline::OnFilterStateTransition, this)); |
987 } else if (state_ == kFlushing) { | 993 } else if (state_ == kFlushing) { |
988 pipeline_filter_->Flush( | 994 pipeline_filter_->Flush( |
989 base::Bind(&Pipeline::OnFilterStateTransition, this)); | 995 base::Bind(&Pipeline::OnFilterStateTransition, this)); |
(...skipping 18 matching lines...) Expand all Loading... |
1008 // the seek has compelted. | 1014 // the seek has compelted. |
1009 if (playback_rate_change_pending_) { | 1015 if (playback_rate_change_pending_) { |
1010 playback_rate_change_pending_ = false; | 1016 playback_rate_change_pending_ = false; |
1011 PlaybackRateChangedTask(pending_playback_rate_); | 1017 PlaybackRateChangedTask(pending_playback_rate_); |
1012 } | 1018 } |
1013 | 1019 |
1014 base::AutoLock auto_lock(lock_); | 1020 base::AutoLock auto_lock(lock_); |
1015 // We use audio stream to update the clock. So if there is such a stream, | 1021 // We use audio stream to update the clock. So if there is such a stream, |
1016 // we pause the clock until we receive a valid timestamp. | 1022 // we pause the clock until we receive a valid timestamp. |
1017 waiting_for_clock_update_ = true; | 1023 waiting_for_clock_update_ = true; |
1018 if (!has_audio_) | 1024 if (!has_audio_) { |
| 1025 clock_->SetMaxTime(clock_->Duration()); |
1019 StartClockIfWaitingForTimeUpdate_Locked(); | 1026 StartClockIfWaitingForTimeUpdate_Locked(); |
| 1027 } |
1020 | 1028 |
1021 // Start monitoring rate of downloading. | 1029 // Start monitoring rate of downloading. |
1022 int bitrate = 0; | 1030 int bitrate = 0; |
1023 if (demuxer_.get()) { | 1031 if (demuxer_.get()) { |
1024 bitrate = demuxer_->GetBitrate(); | 1032 bitrate = demuxer_->GetBitrate(); |
1025 local_source_ = demuxer_->IsLocalSource(); | 1033 local_source_ = demuxer_->IsLocalSource(); |
1026 streaming_ = !demuxer_->IsSeekable(); | 1034 streaming_ = !demuxer_->IsSeekable(); |
1027 } | 1035 } |
1028 // Needs to be locked because most other calls to |download_rate_monitor_| | 1036 // Needs to be locked because most other calls to |download_rate_monitor_| |
1029 // occur on the renderer thread. | 1037 // occur on the renderer thread. |
(...skipping 108 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1138 return; | 1146 return; |
1139 } | 1147 } |
1140 | 1148 |
1141 demuxer_ = demuxer; | 1149 demuxer_ = demuxer; |
1142 demuxer_->set_host(this); | 1150 demuxer_->set_host(this); |
1143 | 1151 |
1144 { | 1152 { |
1145 base::AutoLock auto_lock(lock_); | 1153 base::AutoLock auto_lock(lock_); |
1146 // We do not want to start the clock running. We only want to set the base | 1154 // We do not want to start the clock running. We only want to set the base |
1147 // media time so our timestamp calculations will be correct. | 1155 // media time so our timestamp calculations will be correct. |
1148 clock_->SetTime(demuxer_->GetStartTime()); | 1156 clock_->SetTime(demuxer_->GetStartTime(), demuxer_->GetStartTime()); |
1149 } | 1157 } |
1150 | 1158 |
1151 OnFilterInitialize(PIPELINE_OK); | 1159 OnFilterInitialize(PIPELINE_OK); |
1152 } | 1160 } |
1153 | 1161 |
1154 bool Pipeline::InitializeAudioDecoder( | 1162 bool Pipeline::InitializeAudioDecoder( |
1155 const scoped_refptr<Demuxer>& demuxer) { | 1163 const scoped_refptr<Demuxer>& demuxer) { |
1156 DCHECK_EQ(MessageLoop::current(), message_loop_); | 1164 DCHECK_EQ(MessageLoop::current(), message_loop_); |
1157 DCHECK(IsPipelineOk()); | 1165 DCHECK(IsPipelineOk()); |
1158 | 1166 |
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1226 SetError(PIPELINE_ERROR_REQUIRED_FILTER_MISSING); | 1234 SetError(PIPELINE_ERROR_REQUIRED_FILTER_MISSING); |
1227 return false; | 1235 return false; |
1228 } | 1236 } |
1229 | 1237 |
1230 if (!PrepareFilter(audio_renderer_)) | 1238 if (!PrepareFilter(audio_renderer_)) |
1231 return false; | 1239 return false; |
1232 | 1240 |
1233 audio_renderer_->Initialize( | 1241 audio_renderer_->Initialize( |
1234 decoder, | 1242 decoder, |
1235 base::Bind(&Pipeline::OnFilterInitialize, this, PIPELINE_OK), | 1243 base::Bind(&Pipeline::OnFilterInitialize, this, PIPELINE_OK), |
1236 base::Bind(&Pipeline::OnAudioUnderflow, this)); | 1244 base::Bind(&Pipeline::OnAudioUnderflow, this), |
| 1245 base::Bind(&Pipeline::OnAudioTimeUpdate, this)); |
| 1246 |
1237 return true; | 1247 return true; |
1238 } | 1248 } |
1239 | 1249 |
1240 bool Pipeline::InitializeVideoRenderer( | 1250 bool Pipeline::InitializeVideoRenderer( |
1241 const scoped_refptr<VideoDecoder>& decoder) { | 1251 const scoped_refptr<VideoDecoder>& decoder) { |
1242 DCHECK_EQ(MessageLoop::current(), message_loop_); | 1252 DCHECK_EQ(MessageLoop::current(), message_loop_); |
1243 DCHECK(IsPipelineOk()); | 1253 DCHECK(IsPipelineOk()); |
1244 | 1254 |
1245 if (!decoder) | 1255 if (!decoder) |
1246 return false; | 1256 return false; |
1247 | 1257 |
1248 filter_collection_->SelectVideoRenderer(&video_renderer_); | 1258 filter_collection_->SelectVideoRenderer(&video_renderer_); |
1249 if (!video_renderer_) { | 1259 if (!video_renderer_) { |
1250 SetError(PIPELINE_ERROR_REQUIRED_FILTER_MISSING); | 1260 SetError(PIPELINE_ERROR_REQUIRED_FILTER_MISSING); |
1251 return false; | 1261 return false; |
1252 } | 1262 } |
1253 | 1263 |
1254 if (!PrepareFilter(video_renderer_)) | 1264 if (!PrepareFilter(video_renderer_)) |
1255 return false; | 1265 return false; |
1256 | 1266 |
1257 video_renderer_->Initialize( | 1267 video_renderer_->Initialize( |
1258 decoder, | 1268 decoder, |
1259 base::Bind(&Pipeline::OnFilterInitialize, this, PIPELINE_OK), | 1269 base::Bind(&Pipeline::OnFilterInitialize, this, PIPELINE_OK), |
1260 base::Bind(&Pipeline::OnUpdateStatistics, this)); | 1270 base::Bind(&Pipeline::OnUpdateStatistics, this), |
| 1271 base::Bind(&Pipeline::OnVideoTimeUpdate, this)); |
1261 return true; | 1272 return true; |
1262 } | 1273 } |
1263 | 1274 |
1264 void Pipeline::TearDownPipeline() { | 1275 void Pipeline::TearDownPipeline() { |
1265 DCHECK_EQ(MessageLoop::current(), message_loop_); | 1276 DCHECK_EQ(MessageLoop::current(), message_loop_); |
1266 DCHECK_NE(kStopped, state_); | 1277 DCHECK_NE(kStopped, state_); |
1267 | 1278 |
1268 DCHECK(!tearing_down_ || // Teardown on Stop(). | 1279 DCHECK(!tearing_down_ || // Teardown on Stop(). |
1269 (tearing_down_ && error_caused_teardown_) || // Teardown on error. | 1280 (tearing_down_ && error_caused_teardown_) || // Teardown on error. |
1270 (tearing_down_ && stop_pending_)); // Stop during teardown by error. | 1281 (tearing_down_ && stop_pending_)); // Stop during teardown by error. |
(...skipping 142 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1413 void Pipeline::StartClockIfWaitingForTimeUpdate_Locked() { | 1424 void Pipeline::StartClockIfWaitingForTimeUpdate_Locked() { |
1414 lock_.AssertAcquired(); | 1425 lock_.AssertAcquired(); |
1415 if (!waiting_for_clock_update_) | 1426 if (!waiting_for_clock_update_) |
1416 return; | 1427 return; |
1417 | 1428 |
1418 waiting_for_clock_update_ = false; | 1429 waiting_for_clock_update_ = false; |
1419 clock_->Play(); | 1430 clock_->Play(); |
1420 } | 1431 } |
1421 | 1432 |
1422 } // namespace media | 1433 } // namespace media |
OLD | NEW |