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

Side by Side Diff: media/base/pipeline_impl.cc

Issue 7584013: Log PipelineImpl events to MediaLog. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 9 years, 4 months 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 | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2011 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 // TODO(scherkus): clean up PipelineImpl... too many crazy function names, 5 // TODO(scherkus): clean up PipelineImpl... too many crazy function names,
6 // potential deadlocks, etc... 6 // potential deadlocks, etc...
7 7
8 #include "media/base/pipeline_impl.h" 8 #include "media/base/pipeline_impl.h"
9 9
10 #include <algorithm> 10 #include <algorithm>
11 11
12 #include "base/bind.h" 12 #include "base/bind.h"
13 #include "base/callback.h" 13 #include "base/callback.h"
14 #include "base/compiler_specific.h" 14 #include "base/compiler_specific.h"
15 #include "base/stl_util.h" 15 #include "base/stl_util.h"
16 #include "base/string_util.h" 16 #include "base/string_util.h"
17 #include "base/synchronization/condition_variable.h" 17 #include "base/synchronization/condition_variable.h"
18 #include "media/base/clock.h" 18 #include "media/base/clock.h"
19 #include "media/base/filter_collection.h" 19 #include "media/base/filter_collection.h"
20 #include "media/base/media_log.h"
20 21
21 namespace media { 22 namespace media {
22 23
23 const char kRawMediaScheme[] = "x-raw-media"; 24 const char kRawMediaScheme[] = "x-raw-media";
24 25
25 PipelineStatusNotification::PipelineStatusNotification() 26 PipelineStatusNotification::PipelineStatusNotification()
26 : cv_(&lock_), status_(PIPELINE_OK), notified_(false) { 27 : cv_(&lock_), status_(PIPELINE_OK), notified_(false) {
27 callback_.reset(NewCallback(this, &PipelineStatusNotification::Notify)); 28 callback_.reset(NewCallback(this, &PipelineStatusNotification::Notify));
28 } 29 }
29 30
(...skipping 25 matching lines...) Expand all
55 return status_; 56 return status_;
56 } 57 }
57 58
58 class PipelineImpl::PipelineInitState { 59 class PipelineImpl::PipelineInitState {
59 public: 60 public:
60 scoped_refptr<AudioDecoder> audio_decoder_; 61 scoped_refptr<AudioDecoder> audio_decoder_;
61 scoped_refptr<VideoDecoder> video_decoder_; 62 scoped_refptr<VideoDecoder> video_decoder_;
62 scoped_refptr<CompositeFilter> composite_; 63 scoped_refptr<CompositeFilter> composite_;
63 }; 64 };
64 65
65 PipelineImpl::PipelineImpl(MessageLoop* message_loop) 66 PipelineImpl::PipelineImpl(MessageLoop* message_loop, MediaLog* media_log)
66 : message_loop_(message_loop), 67 : message_loop_(message_loop),
68 media_log_(media_log),
67 clock_(new Clock(&base::Time::Now)), 69 clock_(new Clock(&base::Time::Now)),
68 waiting_for_clock_update_(false), 70 waiting_for_clock_update_(false),
69 state_(kCreated), 71 state_(kCreated),
70 current_bytes_(0) { 72 current_bytes_(0) {
73 media_log_->SetPipelineState(kCreated);
71 ResetState(); 74 ResetState();
75 media_log_->AddEventOfType(MediaLogEvent::PIPELINE_CREATED);
72 } 76 }
73 77
74 PipelineImpl::~PipelineImpl() { 78 PipelineImpl::~PipelineImpl() {
75 base::AutoLock auto_lock(lock_); 79 base::AutoLock auto_lock(lock_);
76 DCHECK(!running_) << "Stop() must complete before destroying object"; 80 DCHECK(!running_) << "Stop() must complete before destroying object";
77 DCHECK(!stop_pending_); 81 DCHECK(!stop_pending_);
78 DCHECK(!seek_pending_); 82 DCHECK(!seek_pending_);
83
84 media_log_->AddEventOfType(MediaLogEvent::PIPELINE_DESTROYED);
79 } 85 }
80 86
81 void PipelineImpl::Init(PipelineStatusCallback* ended_callback, 87 void PipelineImpl::Init(PipelineStatusCallback* ended_callback,
82 PipelineStatusCallback* error_callback, 88 PipelineStatusCallback* error_callback,
83 PipelineStatusCallback* network_callback) { 89 PipelineStatusCallback* network_callback) {
84 DCHECK(!IsRunning()) 90 DCHECK(!IsRunning())
85 << "Init() should be called before the pipeline has started"; 91 << "Init() should be called before the pipeline has started";
86 ended_callback_.reset(ended_callback); 92 ended_callback_.reset(ended_callback);
87 error_callback_.reset(error_callback); 93 error_callback_.reset(error_callback);
88 network_callback_.reset(network_callback); 94 network_callback_.reset(network_callback);
(...skipping 285 matching lines...) Expand 10 before | Expand all | Expand 10 after
374 playback_rate_ = 0.0f; 380 playback_rate_ = 0.0f;
375 pending_playback_rate_ = 0.0f; 381 pending_playback_rate_ = 0.0f;
376 status_ = PIPELINE_OK; 382 status_ = PIPELINE_OK;
377 has_audio_ = false; 383 has_audio_ = false;
378 has_video_ = false; 384 has_video_ = false;
379 waiting_for_clock_update_ = false; 385 waiting_for_clock_update_ = false;
380 audio_disabled_ = false; 386 audio_disabled_ = false;
381 clock_->SetTime(kZero); 387 clock_->SetTime(kZero);
382 } 388 }
383 389
384 void PipelineImpl::set_state(State next_state) { 390 void PipelineImpl::SetState(State next_state) {
385 state_ = next_state; 391 state_ = next_state;
392 media_log_->SetPipelineState(next_state);
386 } 393 }
387 394
388 bool PipelineImpl::IsPipelineOk() { 395 bool PipelineImpl::IsPipelineOk() {
389 base::AutoLock auto_lock(lock_); 396 base::AutoLock auto_lock(lock_);
390 return status_ == PIPELINE_OK; 397 return status_ == PIPELINE_OK;
391 } 398 }
392 399
393 bool PipelineImpl::IsPipelineStopped() { 400 bool PipelineImpl::IsPipelineStopped() {
394 DCHECK_EQ(MessageLoop::current(), message_loop_); 401 DCHECK_EQ(MessageLoop::current(), message_loop_);
395 return state_ == kStopped || state_ == kError; 402 return state_ == kStopped || state_ == kError;
(...skipping 218 matching lines...) Expand 10 before | Expand all | Expand 10 after
614 seek_callback_.reset(start_callback); 621 seek_callback_.reset(start_callback);
615 622
616 // Kick off initialization. 623 // Kick off initialization.
617 pipeline_init_state_.reset(new PipelineInitState()); 624 pipeline_init_state_.reset(new PipelineInitState());
618 pipeline_init_state_->composite_ = new CompositeFilter(message_loop_); 625 pipeline_init_state_->composite_ = new CompositeFilter(message_loop_);
619 pipeline_init_state_->composite_->set_host(this); 626 pipeline_init_state_->composite_->set_host(this);
620 627
621 bool raw_media = (base::strncasecmp(url.c_str(), kRawMediaScheme, 628 bool raw_media = (base::strncasecmp(url.c_str(), kRawMediaScheme,
622 strlen(kRawMediaScheme)) == 0); 629 strlen(kRawMediaScheme)) == 0);
623 if (raw_media) { 630 if (raw_media) {
624 set_state(kInitVideoDecoder); 631 SetState(kInitVideoDecoder);
625 InitializeVideoDecoder(NULL); 632 InitializeVideoDecoder(NULL);
626 } else { 633 } else {
627 set_state(kInitDemuxer); 634 SetState(kInitDemuxer);
628 InitializeDemuxer(); 635 InitializeDemuxer();
629 } 636 }
630 } 637 }
631 638
632 // Main initialization method called on the pipeline thread. This code attempts 639 // Main initialization method called on the pipeline thread. This code attempts
633 // to use the specified filter factory to build a pipeline. 640 // to use the specified filter factory to build a pipeline.
634 // Initialization step performed in this method depends on current state of this 641 // Initialization step performed in this method depends on current state of this
635 // object, indicated by |state_|. After each step of initialization, this 642 // object, indicated by |state_|. After each step of initialization, this
636 // object transits to the next stage. It starts by creating a Demuxer, and then 643 // object transits to the next stage. It starts by creating a Demuxer, and then
637 // connects the Demuxer's audio stream to an AudioDecoder which is then 644 // connects the Demuxer's audio stream to an AudioDecoder which is then
(...skipping 17 matching lines...) Expand all
655 662
656 DCHECK(state_ == kInitDemuxer || 663 DCHECK(state_ == kInitDemuxer ||
657 state_ == kInitAudioDecoder || 664 state_ == kInitAudioDecoder ||
658 state_ == kInitAudioRenderer || 665 state_ == kInitAudioRenderer ||
659 state_ == kInitVideoDecoder || 666 state_ == kInitVideoDecoder ||
660 state_ == kInitVideoRenderer); 667 state_ == kInitVideoRenderer);
661 668
662 669
663 // Demuxer created, create audio decoder. 670 // Demuxer created, create audio decoder.
664 if (state_ == kInitDemuxer) { 671 if (state_ == kInitDemuxer) {
665 set_state(kInitAudioDecoder); 672 SetState(kInitAudioDecoder);
666 // If this method returns false, then there's no audio stream. 673 // If this method returns false, then there's no audio stream.
667 if (InitializeAudioDecoder(demuxer_)) 674 if (InitializeAudioDecoder(demuxer_))
668 return; 675 return;
669 } 676 }
670 677
671 // Assuming audio decoder was created, create audio renderer. 678 // Assuming audio decoder was created, create audio renderer.
672 if (state_ == kInitAudioDecoder) { 679 if (state_ == kInitAudioDecoder) {
673 set_state(kInitAudioRenderer); 680 SetState(kInitAudioRenderer);
681
674 // Returns false if there's no audio stream. 682 // Returns false if there's no audio stream.
675 if (InitializeAudioRenderer(pipeline_init_state_->audio_decoder_)) { 683 if (InitializeAudioRenderer(pipeline_init_state_->audio_decoder_)) {
676 base::AutoLock auto_lock(lock_); 684 base::AutoLock auto_lock(lock_);
677 has_audio_ = true; 685 has_audio_ = true;
678 return; 686 return;
679 } 687 }
680 } 688 }
681 689
682 // Assuming audio renderer was created, create video decoder. 690 // Assuming audio renderer was created, create video decoder.
683 if (state_ == kInitAudioRenderer) { 691 if (state_ == kInitAudioRenderer) {
684 // Then perform the stage of initialization, i.e. initialize video decoder. 692 // Then perform the stage of initialization, i.e. initialize video decoder.
685 set_state(kInitVideoDecoder); 693 SetState(kInitVideoDecoder);
686 if (InitializeVideoDecoder(demuxer_)) 694 if (InitializeVideoDecoder(demuxer_))
687 return; 695 return;
688 } 696 }
689 697
690 // Assuming video decoder was created, create video renderer. 698 // Assuming video decoder was created, create video renderer.
691 if (state_ == kInitVideoDecoder) { 699 if (state_ == kInitVideoDecoder) {
692 set_state(kInitVideoRenderer); 700 SetState(kInitVideoRenderer);
693 if (InitializeVideoRenderer(pipeline_init_state_->video_decoder_)) { 701 if (InitializeVideoRenderer(pipeline_init_state_->video_decoder_)) {
694 base::AutoLock auto_lock(lock_); 702 base::AutoLock auto_lock(lock_);
695 has_video_ = true; 703 has_video_ = true;
696 return; 704 return;
697 } 705 }
698 } 706 }
699 707
700 if (state_ == kInitVideoRenderer) { 708 if (state_ == kInitVideoRenderer) {
701 if (!IsPipelineOk() || !(HasAudio() || HasVideo())) { 709 if (!IsPipelineOk() || !(HasAudio() || HasVideo())) {
702 SetError(PIPELINE_ERROR_COULD_NOT_RENDER); 710 SetError(PIPELINE_ERROR_COULD_NOT_RENDER);
(...skipping 15 matching lines...) Expand all
718 } 726 }
719 727
720 // Initialization was successful, we are now considered paused, so it's safe 728 // Initialization was successful, we are now considered paused, so it's safe
721 // to set the initial playback rate and volume. 729 // to set the initial playback rate and volume.
722 PreloadChangedTask(GetPreload()); 730 PreloadChangedTask(GetPreload());
723 PlaybackRateChangedTask(GetPlaybackRate()); 731 PlaybackRateChangedTask(GetPlaybackRate());
724 VolumeChangedTask(GetVolume()); 732 VolumeChangedTask(GetVolume());
725 733
726 // Fire the seek request to get the filters to preroll. 734 // Fire the seek request to get the filters to preroll.
727 seek_pending_ = true; 735 seek_pending_ = true;
728 set_state(kSeeking); 736 SetState(kSeeking);
729 if (demuxer_) 737 if (demuxer_)
730 seek_timestamp_ = demuxer_->GetStartTime(); 738 seek_timestamp_ = demuxer_->GetStartTime();
731 else 739 else
732 seek_timestamp_ = base::TimeDelta(); 740 seek_timestamp_ = base::TimeDelta();
733 741
734 pipeline_filter_->Seek( 742 pipeline_filter_->Seek(
735 seek_timestamp_, 743 seek_timestamp_,
736 base::Bind(&PipelineImpl::OnFilterStateTransitionWithStatus, this)); 744 base::Bind(&PipelineImpl::OnFilterStateTransitionWithStatus, this));
737 } 745 }
738 } 746 }
(...skipping 118 matching lines...) Expand 10 before | Expand all | Expand 10 after
857 DCHECK(!seek_pending_); 865 DCHECK(!seek_pending_);
858 seek_pending_ = true; 866 seek_pending_ = true;
859 867
860 // We'll need to pause every filter before seeking. The state transition 868 // We'll need to pause every filter before seeking. The state transition
861 // is as follows: 869 // is as follows:
862 // kStarted/kEnded 870 // kStarted/kEnded
863 // kPausing (for each filter) 871 // kPausing (for each filter)
864 // kSeeking (for each filter) 872 // kSeeking (for each filter)
865 // kStarting (for each filter) 873 // kStarting (for each filter)
866 // kStarted 874 // kStarted
867 set_state(kPausing); 875 SetState(kPausing);
868 seek_timestamp_ = time; 876 seek_timestamp_ = time;
869 seek_callback_.reset(seek_callback); 877 seek_callback_.reset(seek_callback);
870 878
871 // Kick off seeking! 879 // Kick off seeking!
872 { 880 {
873 base::AutoLock auto_lock(lock_); 881 base::AutoLock auto_lock(lock_);
874 // If we are waiting for a clock update, the clock hasn't been played yet. 882 // If we are waiting for a clock update, the clock hasn't been played yet.
875 if (!waiting_for_clock_update_) 883 if (!waiting_for_clock_update_)
876 clock_->Pause(); 884 clock_->Pause();
877 } 885 }
(...skipping 23 matching lines...) Expand all
901 waiting_for_clock_update_ = false; 909 waiting_for_clock_update_ = false;
902 clock_->Play(); 910 clock_->Play();
903 } 911 }
904 } 912 }
905 913
906 if (video_renderer_ && !video_renderer_->HasEnded()) { 914 if (video_renderer_ && !video_renderer_->HasEnded()) {
907 return; 915 return;
908 } 916 }
909 917
910 // Transition to ended, executing the callback if present. 918 // Transition to ended, executing the callback if present.
911 set_state(kEnded); 919 SetState(kEnded);
912 if (ended_callback_.get()) { 920 if (ended_callback_.get()) {
913 ended_callback_->Run(status_); 921 ended_callback_->Run(status_);
914 } 922 }
915 } 923 }
916 924
917 void PipelineImpl::NotifyNetworkEventTask() { 925 void PipelineImpl::NotifyNetworkEventTask() {
918 DCHECK_EQ(MessageLoop::current(), message_loop_); 926 DCHECK_EQ(MessageLoop::current(), message_loop_);
919 if (network_callback_.get()) { 927 if (network_callback_.get()) {
920 network_callback_->Run(status_); 928 network_callback_->Run(status_);
921 } 929 }
(...skipping 29 matching lines...) Expand all
951 } 959 }
952 960
953 if (!TransientState(state_)) { 961 if (!TransientState(state_)) {
954 NOTREACHED() << "Invalid current state: " << state_; 962 NOTREACHED() << "Invalid current state: " << state_;
955 SetError(PIPELINE_ERROR_ABORT); 963 SetError(PIPELINE_ERROR_ABORT);
956 return; 964 return;
957 } 965 }
958 966
959 // Decrement the number of remaining transitions, making sure to transition 967 // Decrement the number of remaining transitions, making sure to transition
960 // to the next state if needed. 968 // to the next state if needed.
961 set_state(FindNextState(state_)); 969 SetState(FindNextState(state_));
962 if (state_ == kSeeking) { 970 if (state_ == kSeeking) {
963 base::AutoLock auto_lock(lock_); 971 base::AutoLock auto_lock(lock_);
964 clock_->SetTime(seek_timestamp_); 972 clock_->SetTime(seek_timestamp_);
965 } 973 }
966 974
967 // Carry out the action for the current state. 975 // Carry out the action for the current state.
968 if (TransientState(state_)) { 976 if (TransientState(state_)) {
969 if (state_ == kPausing) { 977 if (state_ == kPausing) {
970 pipeline_filter_->Pause( 978 pipeline_filter_->Pause(
971 NewCallback(this, &PipelineImpl::OnFilterStateTransition)); 979 NewCallback(this, &PipelineImpl::OnFilterStateTransition));
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after
1011 } 1019 }
1012 } else { 1020 } else {
1013 NOTREACHED() << "Unexpected state: " << state_; 1021 NOTREACHED() << "Unexpected state: " << state_;
1014 } 1022 }
1015 } 1023 }
1016 1024
1017 void PipelineImpl::TeardownStateTransitionTask() { 1025 void PipelineImpl::TeardownStateTransitionTask() {
1018 DCHECK(IsPipelineTearingDown()); 1026 DCHECK(IsPipelineTearingDown());
1019 switch (state_) { 1027 switch (state_) {
1020 case kStopping: 1028 case kStopping:
1021 set_state(error_caused_teardown_ ? kError : kStopped); 1029 SetState(error_caused_teardown_ ? kError : kStopped);
1022 FinishDestroyingFiltersTask(); 1030 FinishDestroyingFiltersTask();
1023 break; 1031 break;
1024 case kPausing: 1032 case kPausing:
1025 set_state(kFlushing); 1033 SetState(kFlushing);
1026 pipeline_filter_->Flush( 1034 pipeline_filter_->Flush(
1027 NewCallback(this, &PipelineImpl::OnTeardownStateTransition)); 1035 NewCallback(this, &PipelineImpl::OnTeardownStateTransition));
1028 break; 1036 break;
1029 case kFlushing: 1037 case kFlushing:
1030 set_state(kStopping); 1038 SetState(kStopping);
1031 pipeline_filter_->Stop( 1039 pipeline_filter_->Stop(
1032 NewCallback(this, &PipelineImpl::OnTeardownStateTransition)); 1040 NewCallback(this, &PipelineImpl::OnTeardownStateTransition));
1033 break; 1041 break;
1034 1042
1035 case kCreated: 1043 case kCreated:
1036 case kError: 1044 case kError:
1037 case kInitDemuxer: 1045 case kInitDemuxer:
1038 case kInitAudioDecoder: 1046 case kInitAudioDecoder:
1039 case kInitAudioRenderer: 1047 case kInitAudioRenderer:
1040 case kInitVideoDecoder: 1048 case kInitVideoDecoder:
(...skipping 206 matching lines...) Expand 10 before | Expand all | Expand 10 after
1247 DCHECK(!tearing_down_ || // Teardown on Stop(). 1255 DCHECK(!tearing_down_ || // Teardown on Stop().
1248 (tearing_down_ && error_caused_teardown_) || // Teardown on error. 1256 (tearing_down_ && error_caused_teardown_) || // Teardown on error.
1249 (tearing_down_ && stop_pending_)); // Stop during teardown by error. 1257 (tearing_down_ && stop_pending_)); // Stop during teardown by error.
1250 1258
1251 // Mark that we already start tearing down operation. 1259 // Mark that we already start tearing down operation.
1252 tearing_down_ = true; 1260 tearing_down_ = true;
1253 1261
1254 switch (state_) { 1262 switch (state_) {
1255 case kCreated: 1263 case kCreated:
1256 case kError: 1264 case kError:
1257 set_state(kStopped); 1265 SetState(kStopped);
1258 // Need to put this in the message loop to make sure that it comes 1266 // Need to put this in the message loop to make sure that it comes
1259 // after any pending callback tasks that are already queued. 1267 // after any pending callback tasks that are already queued.
1260 message_loop_->PostTask(FROM_HERE, 1268 message_loop_->PostTask(FROM_HERE,
1261 NewRunnableMethod(this, &PipelineImpl::FinishDestroyingFiltersTask)); 1269 NewRunnableMethod(this, &PipelineImpl::FinishDestroyingFiltersTask));
1262 break; 1270 break;
1263 1271
1264 case kInitDemuxer: 1272 case kInitDemuxer:
1265 case kInitAudioDecoder: 1273 case kInitAudioDecoder:
1266 case kInitAudioRenderer: 1274 case kInitAudioRenderer:
1267 case kInitVideoDecoder: 1275 case kInitVideoDecoder:
1268 case kInitVideoRenderer: 1276 case kInitVideoRenderer:
1269 // Make it look like initialization was successful. 1277 // Make it look like initialization was successful.
1270 pipeline_filter_ = pipeline_init_state_->composite_; 1278 pipeline_filter_ = pipeline_init_state_->composite_;
1271 pipeline_init_state_.reset(); 1279 pipeline_init_state_.reset();
1272 filter_collection_.reset(); 1280 filter_collection_.reset();
1273 1281
1274 set_state(kStopping); 1282 SetState(kStopping);
1275 pipeline_filter_->Stop( 1283 pipeline_filter_->Stop(
1276 NewCallback(this, &PipelineImpl::OnTeardownStateTransition)); 1284 NewCallback(this, &PipelineImpl::OnTeardownStateTransition));
1277 1285
1278 FinishInitialization(); 1286 FinishInitialization();
1279 break; 1287 break;
1280 1288
1281 case kPausing: 1289 case kPausing:
1282 case kSeeking: 1290 case kSeeking:
1283 case kFlushing: 1291 case kFlushing:
1284 case kStarting: 1292 case kStarting:
1285 set_state(kStopping); 1293 SetState(kStopping);
1286 pipeline_filter_->Stop( 1294 pipeline_filter_->Stop(
1287 NewCallback(this, &PipelineImpl::OnTeardownStateTransition)); 1295 NewCallback(this, &PipelineImpl::OnTeardownStateTransition));
1288 1296
1289 if (seek_pending_) { 1297 if (seek_pending_) {
1290 seek_pending_ = false; 1298 seek_pending_ = false;
1291 FinishInitialization(); 1299 FinishInitialization();
1292 } 1300 }
1293 1301
1294 break; 1302 break;
1295 1303
1296 case kStarted: 1304 case kStarted:
1297 case kEnded: 1305 case kEnded:
1298 set_state(kPausing); 1306 SetState(kPausing);
1299 pipeline_filter_->Pause( 1307 pipeline_filter_->Pause(
1300 NewCallback(this, &PipelineImpl::OnTeardownStateTransition)); 1308 NewCallback(this, &PipelineImpl::OnTeardownStateTransition));
1301 break; 1309 break;
1302 1310
1303 case kStopping: 1311 case kStopping:
1304 case kStopped: 1312 case kStopped:
1305 NOTREACHED() << "Unexpected state for teardown: " << state_; 1313 NOTREACHED() << "Unexpected state for teardown: " << state_;
1306 break; 1314 break;
1307 // default: intentionally left out to force new states to cause compiler 1315 // default: intentionally left out to force new states to cause compiler
1308 // errors. 1316 // errors.
1309 }; 1317 };
1310 } 1318 }
1311 1319
1312 } // namespace media 1320 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698