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

Side by Side Diff: content/renderer/media/webmediaplayer_impl.cc

Issue 501473003: Move EME code out of WebMediaPlayerImpl. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Make lint happy Created 6 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 2013 The Chromium Authors. All rights reserved. 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 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 "content/renderer/media/webmediaplayer_impl.h" 5 #include "content/renderer/media/webmediaplayer_impl.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 #include <limits> 8 #include <limits>
9 #include <string> 9 #include <string>
10 #include <vector> 10 #include <vector>
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/callback_helpers.h" 14 #include "base/callback_helpers.h"
15 #include "base/command_line.h" 15 #include "base/command_line.h"
16 #include "base/debug/alias.h" 16 #include "base/debug/alias.h"
17 #include "base/debug/crash_logging.h" 17 #include "base/debug/crash_logging.h"
18 #include "base/debug/trace_event.h" 18 #include "base/debug/trace_event.h"
19 #include "base/message_loop/message_loop_proxy.h" 19 #include "base/message_loop/message_loop_proxy.h"
20 #include "base/metrics/histogram.h" 20 #include "base/metrics/histogram.h"
21 #include "base/strings/string_number_conversions.h"
22 #include "base/strings/utf_string_conversions.h"
23 #include "base/synchronization/waitable_event.h" 21 #include "base/synchronization/waitable_event.h"
24 #include "cc/blink/web_layer_impl.h" 22 #include "cc/blink/web_layer_impl.h"
25 #include "cc/layers/video_layer.h" 23 #include "cc/layers/video_layer.h"
26 #include "content/public/common/content_switches.h" 24 #include "content/public/common/content_switches.h"
27 #include "content/public/renderer/render_frame.h" 25 #include "content/public/renderer/render_frame.h"
28 #include "content/renderer/media/buffered_data_source.h" 26 #include "content/renderer/media/buffered_data_source.h"
29 #include "content/renderer/media/crypto/key_systems.h" 27 #include "content/renderer/media/crypto/encrypted_media_support.h"
30 #include "content/renderer/media/render_media_log.h" 28 #include "content/renderer/media/render_media_log.h"
31 #include "content/renderer/media/texttrack_impl.h" 29 #include "content/renderer/media/texttrack_impl.h"
32 #include "content/renderer/media/webaudiosourceprovider_impl.h" 30 #include "content/renderer/media/webaudiosourceprovider_impl.h"
33 #include "content/renderer/media/webcontentdecryptionmodule_impl.h"
34 #include "content/renderer/media/webinbandtexttrack_impl.h" 31 #include "content/renderer/media/webinbandtexttrack_impl.h"
35 #include "content/renderer/media/webmediaplayer_delegate.h" 32 #include "content/renderer/media/webmediaplayer_delegate.h"
36 #include "content/renderer/media/webmediaplayer_params.h" 33 #include "content/renderer/media/webmediaplayer_params.h"
37 #include "content/renderer/media/webmediaplayer_util.h" 34 #include "content/renderer/media/webmediaplayer_util.h"
38 #include "content/renderer/media/webmediasource_impl.h" 35 #include "content/renderer/media/webmediasource_impl.h"
39 #include "content/renderer/pepper/pepper_webplugin_impl.h"
40 #include "content/renderer/render_thread_impl.h" 36 #include "content/renderer/render_thread_impl.h"
41 #include "gpu/GLES2/gl2extchromium.h" 37 #include "gpu/GLES2/gl2extchromium.h"
42 #include "gpu/command_buffer/common/mailbox_holder.h" 38 #include "gpu/command_buffer/common/mailbox_holder.h"
43 #include "media/audio/null_audio_sink.h" 39 #include "media/audio/null_audio_sink.h"
44 #include "media/base/audio_hardware_config.h" 40 #include "media/base/audio_hardware_config.h"
45 #include "media/base/bind_to_current_loop.h" 41 #include "media/base/bind_to_current_loop.h"
46 #include "media/base/filter_collection.h" 42 #include "media/base/filter_collection.h"
47 #include "media/base/limits.h" 43 #include "media/base/limits.h"
48 #include "media/base/media_log.h" 44 #include "media/base/media_log.h"
49 #include "media/base/media_switches.h" 45 #include "media/base/media_switches.h"
50 #include "media/base/pipeline.h" 46 #include "media/base/pipeline.h"
51 #include "media/base/text_renderer.h" 47 #include "media/base/text_renderer.h"
52 #include "media/base/video_frame.h" 48 #include "media/base/video_frame.h"
53 #include "media/filters/audio_renderer_impl.h" 49 #include "media/filters/audio_renderer_impl.h"
54 #include "media/filters/chunk_demuxer.h" 50 #include "media/filters/chunk_demuxer.h"
55 #include "media/filters/ffmpeg_audio_decoder.h" 51 #include "media/filters/ffmpeg_audio_decoder.h"
56 #include "media/filters/ffmpeg_demuxer.h" 52 #include "media/filters/ffmpeg_demuxer.h"
57 #include "media/filters/ffmpeg_video_decoder.h" 53 #include "media/filters/ffmpeg_video_decoder.h"
58 #include "media/filters/gpu_video_accelerator_factories.h" 54 #include "media/filters/gpu_video_accelerator_factories.h"
59 #include "media/filters/gpu_video_decoder.h" 55 #include "media/filters/gpu_video_decoder.h"
60 #include "media/filters/opus_audio_decoder.h" 56 #include "media/filters/opus_audio_decoder.h"
61 #include "media/filters/video_renderer_impl.h" 57 #include "media/filters/video_renderer_impl.h"
62 #include "media/filters/vpx_video_decoder.h" 58 #include "media/filters/vpx_video_decoder.h"
63 #include "third_party/WebKit/public/platform/WebContentDecryptionModule.h"
64 #include "third_party/WebKit/public/platform/WebContentDecryptionModuleResult.h"
65 #include "third_party/WebKit/public/platform/WebMediaSource.h" 59 #include "third_party/WebKit/public/platform/WebMediaSource.h"
66 #include "third_party/WebKit/public/platform/WebRect.h" 60 #include "third_party/WebKit/public/platform/WebRect.h"
67 #include "third_party/WebKit/public/platform/WebSize.h" 61 #include "third_party/WebKit/public/platform/WebSize.h"
68 #include "third_party/WebKit/public/platform/WebString.h" 62 #include "third_party/WebKit/public/platform/WebString.h"
69 #include "third_party/WebKit/public/platform/WebURL.h" 63 #include "third_party/WebKit/public/platform/WebURL.h"
70 #include "third_party/WebKit/public/web/WebDocument.h"
71 #include "third_party/WebKit/public/web/WebLocalFrame.h" 64 #include "third_party/WebKit/public/web/WebLocalFrame.h"
72 #include "third_party/WebKit/public/web/WebRuntimeFeatures.h"
73 #include "third_party/WebKit/public/web/WebSecurityOrigin.h" 65 #include "third_party/WebKit/public/web/WebSecurityOrigin.h"
74 #include "third_party/WebKit/public/web/WebView.h" 66 #include "third_party/WebKit/public/web/WebView.h"
75 #include "v8/include/v8.h" 67 #include "v8/include/v8.h"
76 68
77 #if defined(ENABLE_PEPPER_CDMS)
78 #include "content/renderer/media/crypto/pepper_cdm_wrapper_impl.h"
79 #endif
80
81 using blink::WebCanvas; 69 using blink::WebCanvas;
82 using blink::WebMediaPlayer; 70 using blink::WebMediaPlayer;
83 using blink::WebRect; 71 using blink::WebRect;
84 using blink::WebSize; 72 using blink::WebSize;
85 using blink::WebString; 73 using blink::WebString;
86 using media::PipelineStatus; 74 using media::PipelineStatus;
87 75
88 namespace { 76 namespace {
89 77
90 // Amount of extra memory used by each player instance reported to V8. 78 // Amount of extra memory used by each player instance reported to V8.
(...skipping 18 matching lines...) Expand all
109 // 97 //
110 // A very slow speed, ie 0.00000001x, causes the machine to lock up. (It seems 98 // A very slow speed, ie 0.00000001x, causes the machine to lock up. (It seems
111 // like a busy loop). It gets unresponsive, although its not completely dead. 99 // like a busy loop). It gets unresponsive, although its not completely dead.
112 // 100 //
113 // Also our timers are not very accurate (especially for ogg), which becomes 101 // Also our timers are not very accurate (especially for ogg), which becomes
114 // evident at low speeds and on Vista. Since other speeds are risky and outside 102 // evident at low speeds and on Vista. Since other speeds are risky and outside
115 // the norms, we think 1/16x to 16x is a safe and useful range for now. 103 // the norms, we think 1/16x to 16x is a safe and useful range for now.
116 const double kMinRate = 0.0625; 104 const double kMinRate = 0.0625;
117 const double kMaxRate = 16.0; 105 const double kMaxRate = 16.0;
118 106
119 // Prefix for histograms related to Encrypted Media Extensions.
120 const char* kMediaEme = "Media.EME.";
121
122 class SyncPointClientImpl : public media::VideoFrame::SyncPointClient { 107 class SyncPointClientImpl : public media::VideoFrame::SyncPointClient {
123 public: 108 public:
124 explicit SyncPointClientImpl( 109 explicit SyncPointClientImpl(
125 blink::WebGraphicsContext3D* web_graphics_context) 110 blink::WebGraphicsContext3D* web_graphics_context)
126 : web_graphics_context_(web_graphics_context) {} 111 : web_graphics_context_(web_graphics_context) {}
127 virtual ~SyncPointClientImpl() {} 112 virtual ~SyncPointClientImpl() {}
128 virtual uint32 InsertSyncPoint() OVERRIDE { 113 virtual uint32 InsertSyncPoint() OVERRIDE {
129 return web_graphics_context_->insertSyncPoint(); 114 return web_graphics_context_->insertSyncPoint();
130 } 115 }
131 virtual void WaitSyncPoint(uint32 sync_point) OVERRIDE { 116 virtual void WaitSyncPoint(uint32 sync_point) OVERRIDE {
132 web_graphics_context_->waitSyncPoint(sync_point); 117 web_graphics_context_->waitSyncPoint(sync_point);
133 } 118 }
134 119
135 private: 120 private:
136 blink::WebGraphicsContext3D* web_graphics_context_; 121 blink::WebGraphicsContext3D* web_graphics_context_;
137 }; 122 };
138 123
139 // Used for calls to decryptor_ready_cb where the result can be ignored.
140 void DoNothing(bool) {
141 }
142
143 } // namespace 124 } // namespace
144 125
145 namespace content { 126 namespace content {
146 127
147 class BufferedDataSourceHostImpl; 128 class BufferedDataSourceHostImpl;
148 129
149 #define COMPILE_ASSERT_MATCHING_ENUM(name) \ 130 #define COMPILE_ASSERT_MATCHING_ENUM(name) \
150 COMPILE_ASSERT(static_cast<int>(WebMediaPlayer::CORSMode ## name) == \ 131 COMPILE_ASSERT(static_cast<int>(WebMediaPlayer::CORSMode ## name) == \
151 static_cast<int>(BufferedResourceLoader::k ## name), \ 132 static_cast<int>(BufferedResourceLoader::k ## name), \
152 mismatching_enums) 133 mismatching_enums)
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after
199 chunk_demuxer_(NULL), 180 chunk_demuxer_(NULL),
200 // Threaded compositing isn't enabled universally yet. 181 // Threaded compositing isn't enabled universally yet.
201 compositor_task_runner_( 182 compositor_task_runner_(
202 RenderThreadImpl::current()->compositor_message_loop_proxy() 183 RenderThreadImpl::current()->compositor_message_loop_proxy()
203 ? RenderThreadImpl::current()->compositor_message_loop_proxy() 184 ? RenderThreadImpl::current()->compositor_message_loop_proxy()
204 : base::MessageLoopProxy::current()), 185 : base::MessageLoopProxy::current()),
205 compositor_(new VideoFrameCompositor( 186 compositor_(new VideoFrameCompositor(
206 BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnNaturalSizeChanged), 187 BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnNaturalSizeChanged),
207 BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnOpacityChanged))), 188 BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnOpacityChanged))),
208 text_track_index_(0), 189 text_track_index_(0),
209 web_cdm_(NULL) { 190 encrypted_media_support_(EncryptedMediaSupport::create(client)) {
210 media_log_->AddEvent( 191 media_log_->AddEvent(
211 media_log_->CreateEvent(media::MediaLogEvent::WEBMEDIAPLAYER_CREATED)); 192 media_log_->CreateEvent(media::MediaLogEvent::WEBMEDIAPLAYER_CREATED));
212 193
213 // |gpu_factories_| requires that its entry points be called on its 194 // |gpu_factories_| requires that its entry points be called on its
214 // |GetTaskRunner()|. Since |pipeline_| will own decoders created from the 195 // |GetTaskRunner()|. Since |pipeline_| will own decoders created from the
215 // factories, require that their message loops are identical. 196 // factories, require that their message loops are identical.
216 DCHECK(!gpu_factories_ || (gpu_factories_->GetTaskRunner() == media_loop_)); 197 DCHECK(!gpu_factories_ || (gpu_factories_->GetTaskRunner() == media_loop_));
217 198
218 // Let V8 know we started new thread if we did not do it yet. 199 // Let V8 know we started new thread if we did not do it yet.
219 // Made separate task to avoid deletion of player currently being created. 200 // Made separate task to avoid deletion of player currently being created.
(...skipping 429 matching lines...) Expand 10 before | Expand all | Expand 10 after
649 false); 630 false);
650 631
651 web_graphics_context->deleteTexture(source_texture); 632 web_graphics_context->deleteTexture(source_texture);
652 web_graphics_context->flush(); 633 web_graphics_context->flush();
653 634
654 SyncPointClientImpl client(web_graphics_context); 635 SyncPointClientImpl client(web_graphics_context);
655 video_frame->UpdateReleaseSyncPoint(&client); 636 video_frame->UpdateReleaseSyncPoint(&client);
656 return true; 637 return true;
657 } 638 }
658 639
659 // Helper functions to report media EME related stats to UMA. They follow the
660 // convention of more commonly used macros UMA_HISTOGRAM_ENUMERATION and
661 // UMA_HISTOGRAM_COUNTS. The reason that we cannot use those macros directly is
662 // that UMA_* macros require the names to be constant throughout the process'
663 // lifetime.
664 static void EmeUMAHistogramEnumeration(const std::string& key_system,
665 const std::string& method,
666 int sample,
667 int boundary_value) {
668 base::LinearHistogram::FactoryGet(
669 kMediaEme + KeySystemNameForUMA(key_system) + "." + method,
670 1, boundary_value, boundary_value + 1,
671 base::Histogram::kUmaTargetedHistogramFlag)->Add(sample);
672 }
673
674 static void EmeUMAHistogramCounts(const std::string& key_system,
675 const std::string& method,
676 int sample) {
677 // Use the same parameters as UMA_HISTOGRAM_COUNTS.
678 base::Histogram::FactoryGet(
679 kMediaEme + KeySystemNameForUMA(key_system) + "." + method,
680 1, 1000000, 50, base::Histogram::kUmaTargetedHistogramFlag)->Add(sample);
681 }
682
683 // Helper enum for reporting generateKeyRequest/addKey histograms.
684 enum MediaKeyException {
685 kUnknownResultId,
686 kSuccess,
687 kKeySystemNotSupported,
688 kInvalidPlayerState,
689 kMaxMediaKeyException
690 };
691
692 static MediaKeyException MediaKeyExceptionForUMA(
693 WebMediaPlayer::MediaKeyException e) {
694 switch (e) {
695 case WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported:
696 return kKeySystemNotSupported;
697 case WebMediaPlayer::MediaKeyExceptionInvalidPlayerState:
698 return kInvalidPlayerState;
699 case WebMediaPlayer::MediaKeyExceptionNoError:
700 return kSuccess;
701 default:
702 return kUnknownResultId;
703 }
704 }
705
706 // Helper for converting |key_system| name and exception |e| to a pair of enum
707 // values from above, for reporting to UMA.
708 static void ReportMediaKeyExceptionToUMA(const std::string& method,
709 const std::string& key_system,
710 WebMediaPlayer::MediaKeyException e) {
711 MediaKeyException result_id = MediaKeyExceptionForUMA(e);
712 DCHECK_NE(result_id, kUnknownResultId) << e;
713 EmeUMAHistogramEnumeration(
714 key_system, method, result_id, kMaxMediaKeyException);
715 }
716
717 // Convert a WebString to ASCII, falling back on an empty string in the case
718 // of a non-ASCII string.
719 static std::string ToASCIIOrEmpty(const blink::WebString& string) {
720 return base::IsStringASCII(string) ? base::UTF16ToASCII(string)
721 : std::string();
722 }
723
724 WebMediaPlayer::MediaKeyException 640 WebMediaPlayer::MediaKeyException
725 WebMediaPlayerImpl::generateKeyRequest(const WebString& key_system, 641 WebMediaPlayerImpl::generateKeyRequest(const WebString& key_system,
726 const unsigned char* init_data, 642 const unsigned char* init_data,
727 unsigned init_data_length) { 643 unsigned init_data_length) {
728 DVLOG(1) << "generateKeyRequest: " << base::string16(key_system) << ": "
729 << std::string(reinterpret_cast<const char*>(init_data),
730 static_cast<size_t>(init_data_length));
731
732 std::string ascii_key_system =
733 GetUnprefixedKeySystemName(ToASCIIOrEmpty(key_system));
734
735 WebMediaPlayer::MediaKeyException e =
736 GenerateKeyRequestInternal(ascii_key_system, init_data, init_data_length);
737 ReportMediaKeyExceptionToUMA("generateKeyRequest", ascii_key_system, e);
738 return e;
739 }
740
741 // Guess the type of |init_data|. This is only used to handle some corner cases
742 // so we keep it as simple as possible without breaking major use cases.
743 static std::string GuessInitDataType(const unsigned char* init_data,
744 unsigned init_data_length) {
745 // Most WebM files use KeyId of 16 bytes. MP4 init data are always >16 bytes.
746 if (init_data_length == 16)
747 return "video/webm";
748
749 return "video/mp4";
750 }
751
752 WebMediaPlayer::MediaKeyException
753 WebMediaPlayerImpl::GenerateKeyRequestInternal(const std::string& key_system,
754 const unsigned char* init_data,
755 unsigned init_data_length) {
756 DCHECK(main_loop_->BelongsToCurrentThread()); 644 DCHECK(main_loop_->BelongsToCurrentThread());
757 645
758 if (!IsConcreteSupportedKeySystem(key_system)) 646 if (!encrypted_media_support_)
ddorwin 2014/08/22 21:08:48 Is this possible given line 190? Ditto below.
acolwell GONE FROM CHROMIUM 2014/08/22 23:20:22 Yes. That was the idea, but now that I think of it
759 return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported; 647 return MediaKeyExceptionInvalidPlayerState;
760 648
761 // We do not support run-time switching between key systems for now. 649 return encrypted_media_support_->generateKeyRequest(
762 if (current_key_system_.empty()) { 650 frame_, key_system, init_data, init_data_length);
763 if (!proxy_decryptor_) {
764 proxy_decryptor_.reset(new ProxyDecryptor(
765 #if defined(ENABLE_PEPPER_CDMS)
766 // Create() must be called synchronously as |frame_| may not be
767 // valid afterwards.
768 base::Bind(&PepperCdmWrapperImpl::Create, frame_),
769 #elif defined(ENABLE_BROWSER_CDMS)
770 #error Browser side CDM in WMPI for prefixed EME API not supported yet.
771 #endif
772 BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnKeyAdded),
773 BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnKeyError),
774 BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnKeyMessage)));
775 }
776
777 GURL security_origin(frame_->document().securityOrigin().toString());
778 if (!proxy_decryptor_->InitializeCDM(key_system, security_origin))
779 return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported;
780
781 if (proxy_decryptor_ && !decryptor_ready_cb_.is_null()) {
782 base::ResetAndReturn(&decryptor_ready_cb_)
783 .Run(proxy_decryptor_->GetDecryptor(), base::Bind(DoNothing));
784 }
785
786 current_key_system_ = key_system;
787 } else if (key_system != current_key_system_) {
788 return WebMediaPlayer::MediaKeyExceptionInvalidPlayerState;
789 }
790
791 std::string init_data_type = init_data_type_;
792 if (init_data_type.empty())
793 init_data_type = GuessInitDataType(init_data, init_data_length);
794
795 // TODO(xhwang): We assume all streams are from the same container (thus have
796 // the same "type") for now. In the future, the "type" should be passed down
797 // from the application.
798 if (!proxy_decryptor_->GenerateKeyRequest(
799 init_data_type, init_data, init_data_length)) {
800 current_key_system_.clear();
801 return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported;
802 }
803
804 return WebMediaPlayer::MediaKeyExceptionNoError;
805 } 651 }
806 652
807 WebMediaPlayer::MediaKeyException WebMediaPlayerImpl::addKey( 653 WebMediaPlayer::MediaKeyException WebMediaPlayerImpl::addKey(
808 const WebString& key_system, 654 const WebString& key_system,
809 const unsigned char* key, 655 const unsigned char* key,
810 unsigned key_length, 656 unsigned key_length,
811 const unsigned char* init_data, 657 const unsigned char* init_data,
812 unsigned init_data_length, 658 unsigned init_data_length,
813 const WebString& session_id) { 659 const WebString& session_id) {
814 DVLOG(1) << "addKey: " << base::string16(key_system) << ": " 660 DCHECK(main_loop_->BelongsToCurrentThread());
815 << std::string(reinterpret_cast<const char*>(key),
816 static_cast<size_t>(key_length)) << ", "
817 << std::string(reinterpret_cast<const char*>(init_data),
818 static_cast<size_t>(init_data_length)) << " ["
819 << base::string16(session_id) << "]";
820 661
821 std::string ascii_key_system = 662 if (!encrypted_media_support_)
822 GetUnprefixedKeySystemName(ToASCIIOrEmpty(key_system)); 663 return MediaKeyExceptionInvalidPlayerState;
823 std::string ascii_session_id = ToASCIIOrEmpty(session_id);
824 664
825 WebMediaPlayer::MediaKeyException e = AddKeyInternal(ascii_key_system, 665 return encrypted_media_support_->addKey(
826 key, 666 key_system, key, key_length, init_data, init_data_length, session_id);
827 key_length,
828 init_data,
829 init_data_length,
830 ascii_session_id);
831 ReportMediaKeyExceptionToUMA("addKey", ascii_key_system, e);
832 return e;
833 }
834
835 WebMediaPlayer::MediaKeyException WebMediaPlayerImpl::AddKeyInternal(
836 const std::string& key_system,
837 const unsigned char* key,
838 unsigned key_length,
839 const unsigned char* init_data,
840 unsigned init_data_length,
841 const std::string& session_id) {
842 DCHECK(key);
843 DCHECK_GT(key_length, 0u);
844
845 if (!IsConcreteSupportedKeySystem(key_system))
846 return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported;
847
848 if (current_key_system_.empty() || key_system != current_key_system_)
849 return WebMediaPlayer::MediaKeyExceptionInvalidPlayerState;
850
851 proxy_decryptor_->AddKey(
852 key, key_length, init_data, init_data_length, session_id);
853 return WebMediaPlayer::MediaKeyExceptionNoError;
854 } 667 }
855 668
856 WebMediaPlayer::MediaKeyException WebMediaPlayerImpl::cancelKeyRequest( 669 WebMediaPlayer::MediaKeyException WebMediaPlayerImpl::cancelKeyRequest(
857 const WebString& key_system, 670 const WebString& key_system,
858 const WebString& session_id) { 671 const WebString& session_id) {
859 DVLOG(1) << "cancelKeyRequest: " << base::string16(key_system) << ": " 672 DCHECK(main_loop_->BelongsToCurrentThread());
860 << " [" << base::string16(session_id) << "]";
861 673
862 std::string ascii_key_system = 674 if (!encrypted_media_support_)
863 GetUnprefixedKeySystemName(ToASCIIOrEmpty(key_system)); 675 return MediaKeyExceptionInvalidPlayerState;
864 std::string ascii_session_id = ToASCIIOrEmpty(session_id);
865 676
866 WebMediaPlayer::MediaKeyException e = 677 return encrypted_media_support_->cancelKeyRequest(key_system, session_id);
867 CancelKeyRequestInternal(ascii_key_system, ascii_session_id);
868 ReportMediaKeyExceptionToUMA("cancelKeyRequest", ascii_key_system, e);
869 return e;
870 }
871
872 WebMediaPlayer::MediaKeyException WebMediaPlayerImpl::CancelKeyRequestInternal(
873 const std::string& key_system,
874 const std::string& session_id) {
875 if (!IsConcreteSupportedKeySystem(key_system))
876 return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported;
877
878 if (current_key_system_.empty() || key_system != current_key_system_)
879 return WebMediaPlayer::MediaKeyExceptionInvalidPlayerState;
880
881 proxy_decryptor_->CancelKeyRequest(session_id);
882 return WebMediaPlayer::MediaKeyExceptionNoError;
883 } 678 }
884 679
885 void WebMediaPlayerImpl::setContentDecryptionModule( 680 void WebMediaPlayerImpl::setContentDecryptionModule(
886 blink::WebContentDecryptionModule* cdm) { 681 blink::WebContentDecryptionModule* cdm) {
887 DCHECK(main_loop_->BelongsToCurrentThread()); 682 DCHECK(main_loop_->BelongsToCurrentThread());
888 683
889 // TODO(xhwang): Support setMediaKeys(0) if necessary: http://crbug.com/330324 684 if (!encrypted_media_support_)
890 if (!cdm)
891 return; 685 return;
892 686
893 web_cdm_ = ToWebContentDecryptionModuleImpl(cdm); 687 encrypted_media_support_->setContentDecryptionModule(cdm);
894
895 if (web_cdm_ && !decryptor_ready_cb_.is_null())
896 base::ResetAndReturn(&decryptor_ready_cb_)
897 .Run(web_cdm_->GetDecryptor(), base::Bind(DoNothing));
898 } 688 }
899 689
900 void WebMediaPlayerImpl::setContentDecryptionModule( 690 void WebMediaPlayerImpl::setContentDecryptionModule(
901 blink::WebContentDecryptionModule* cdm, 691 blink::WebContentDecryptionModule* cdm,
902 blink::WebContentDecryptionModuleResult result) { 692 blink::WebContentDecryptionModuleResult result) {
903 DCHECK(main_loop_->BelongsToCurrentThread()); 693 DCHECK(main_loop_->BelongsToCurrentThread());
904 694
905 // TODO(xhwang): Support setMediaKeys(0) if necessary: http://crbug.com/330324 695 if (!encrypted_media_support_)
906 if (!cdm) {
907 result.completeWithError(
908 blink::WebContentDecryptionModuleExceptionNotSupportedError,
909 0,
910 "Null MediaKeys object is not supported.");
911 return; 696 return;
912 }
913 697
914 web_cdm_ = ToWebContentDecryptionModuleImpl(cdm); 698 encrypted_media_support_->setContentDecryptionModule(cdm, result);
915
916 if (web_cdm_ && !decryptor_ready_cb_.is_null()) {
917 base::ResetAndReturn(&decryptor_ready_cb_)
918 .Run(web_cdm_->GetDecryptor(),
919 BIND_TO_RENDER_LOOP1(
920 &WebMediaPlayerImpl::ContentDecryptionModuleAttached, result));
921 } else {
922 // No pipeline/decoder connected, so resolve the promise. When something
923 // is connected, setting the CDM will happen in SetDecryptorReadyCB().
924 ContentDecryptionModuleAttached(result, true);
925 }
926 } 699 }
927 700
928 void WebMediaPlayerImpl::setContentDecryptionModuleSync( 701 void WebMediaPlayerImpl::setContentDecryptionModuleSync(
929 blink::WebContentDecryptionModule* cdm) { 702 blink::WebContentDecryptionModule* cdm) {
930 DCHECK(main_loop_->BelongsToCurrentThread()); 703 DCHECK(main_loop_->BelongsToCurrentThread());
931 704
932 // Used when loading media and no pipeline/decoder attached yet. 705 if (!encrypted_media_support_)
933 DCHECK(decryptor_ready_cb_.is_null()); 706 return;
934 707
935 web_cdm_ = ToWebContentDecryptionModuleImpl(cdm); 708 encrypted_media_support_->setContentDecryptionModuleSync(cdm);
936 }
937
938 void WebMediaPlayerImpl::ContentDecryptionModuleAttached(
939 blink::WebContentDecryptionModuleResult result,
940 bool success) {
941 if (success) {
942 result.complete();
943 return;
944 }
945
946 result.completeWithError(
947 blink::WebContentDecryptionModuleExceptionNotSupportedError,
948 0,
949 "Unable to set MediaKeys object");
950 } 709 }
951 710
952 void WebMediaPlayerImpl::OnPipelineSeeked(bool time_changed, 711 void WebMediaPlayerImpl::OnPipelineSeeked(bool time_changed,
953 PipelineStatus status) { 712 PipelineStatus status) {
954 DVLOG(1) << __FUNCTION__ << "(" << time_changed << ", " << status << ")"; 713 DVLOG(1) << __FUNCTION__ << "(" << time_changed << ", " << status << ")";
955 DCHECK(main_loop_->BelongsToCurrentThread()); 714 DCHECK(main_loop_->BelongsToCurrentThread());
956 seeking_ = false; 715 seeking_ = false;
957 if (pending_seek_) { 716 if (pending_seek_) {
958 pending_seek_ = false; 717 pending_seek_ = false;
959 seek(pending_seek_seconds_); 718 seek(pending_seek_seconds_);
(...skipping 24 matching lines...) Expand all
984 743
985 if (ready_state_ == WebMediaPlayer::ReadyStateHaveNothing) { 744 if (ready_state_ == WebMediaPlayer::ReadyStateHaveNothing) {
986 // Any error that occurs before reaching ReadyStateHaveMetadata should 745 // Any error that occurs before reaching ReadyStateHaveMetadata should
987 // be considered a format error. 746 // be considered a format error.
988 SetNetworkState(WebMediaPlayer::NetworkStateFormatError); 747 SetNetworkState(WebMediaPlayer::NetworkStateFormatError);
989 return; 748 return;
990 } 749 }
991 750
992 SetNetworkState(PipelineErrorToNetworkState(error)); 751 SetNetworkState(PipelineErrorToNetworkState(error));
993 752
994 if (error == media::PIPELINE_ERROR_DECRYPT) 753 if (error == media::PIPELINE_ERROR_DECRYPT && encrypted_media_support_)
995 EmeUMAHistogramCounts(current_key_system_, "DecryptError", 1); 754 encrypted_media_support_->OnDecryptError();
996 } 755 }
997 756
998 void WebMediaPlayerImpl::OnPipelineMetadata( 757 void WebMediaPlayerImpl::OnPipelineMetadata(
999 media::PipelineMetadata metadata) { 758 media::PipelineMetadata metadata) {
1000 DVLOG(1) << __FUNCTION__; 759 DVLOG(1) << __FUNCTION__;
1001 760
1002 pipeline_metadata_ = metadata; 761 pipeline_metadata_ = metadata;
1003 762
1004 UMA_HISTOGRAM_ENUMERATION("Media.VideoRotation", 763 UMA_HISTOGRAM_ENUMERATION("Media.VideoRotation",
1005 metadata.video_rotation, 764 metadata.video_rotation,
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after
1040 if (should_notify_time_changed_) 799 if (should_notify_time_changed_)
1041 client_->timeChanged(); 800 client_->timeChanged();
1042 } 801 }
1043 802
1044 void WebMediaPlayerImpl::OnDemuxerOpened() { 803 void WebMediaPlayerImpl::OnDemuxerOpened() {
1045 DCHECK(main_loop_->BelongsToCurrentThread()); 804 DCHECK(main_loop_->BelongsToCurrentThread());
1046 client_->mediaSourceOpened(new WebMediaSourceImpl( 805 client_->mediaSourceOpened(new WebMediaSourceImpl(
1047 chunk_demuxer_, base::Bind(&LogMediaSourceError, media_log_))); 806 chunk_demuxer_, base::Bind(&LogMediaSourceError, media_log_)));
1048 } 807 }
1049 808
1050 void WebMediaPlayerImpl::OnKeyAdded(const std::string& session_id) {
1051 DCHECK(main_loop_->BelongsToCurrentThread());
1052 EmeUMAHistogramCounts(current_key_system_, "KeyAdded", 1);
1053 client_->keyAdded(
1054 WebString::fromUTF8(GetPrefixedKeySystemName(current_key_system_)),
1055 WebString::fromUTF8(session_id));
1056 }
1057
1058 void WebMediaPlayerImpl::OnNeedKey(const std::string& type,
1059 const std::vector<uint8>& init_data) {
1060 DCHECK(main_loop_->BelongsToCurrentThread());
1061
1062 // Do not fire NeedKey event if encrypted media is not enabled.
1063 if (!blink::WebRuntimeFeatures::isPrefixedEncryptedMediaEnabled() &&
1064 !blink::WebRuntimeFeatures::isEncryptedMediaEnabled()) {
1065 return;
1066 }
1067
1068 UMA_HISTOGRAM_COUNTS(kMediaEme + std::string("NeedKey"), 1);
1069
1070 DCHECK(init_data_type_.empty() || type.empty() || type == init_data_type_);
1071 if (init_data_type_.empty())
1072 init_data_type_ = type;
1073
1074 const uint8* init_data_ptr = init_data.empty() ? NULL : &init_data[0];
1075 client_->keyNeeded(
1076 WebString::fromUTF8(type), init_data_ptr, init_data.size());
1077 }
1078
1079 void WebMediaPlayerImpl::OnAddTextTrack( 809 void WebMediaPlayerImpl::OnAddTextTrack(
1080 const media::TextTrackConfig& config, 810 const media::TextTrackConfig& config,
1081 const media::AddTextTrackDoneCB& done_cb) { 811 const media::AddTextTrackDoneCB& done_cb) {
1082 DCHECK(main_loop_->BelongsToCurrentThread()); 812 DCHECK(main_loop_->BelongsToCurrentThread());
1083 813
1084 const WebInbandTextTrackImpl::Kind web_kind = 814 const WebInbandTextTrackImpl::Kind web_kind =
1085 static_cast<WebInbandTextTrackImpl::Kind>(config.kind()); 815 static_cast<WebInbandTextTrackImpl::Kind>(config.kind());
1086 const blink::WebString web_label = 816 const blink::WebString web_label =
1087 blink::WebString::fromUTF8(config.label()); 817 blink::WebString::fromUTF8(config.label());
1088 const blink::WebString web_language = 818 const blink::WebString web_language =
1089 blink::WebString::fromUTF8(config.language()); 819 blink::WebString::fromUTF8(config.language());
1090 const blink::WebString web_id = 820 const blink::WebString web_id =
1091 blink::WebString::fromUTF8(config.id()); 821 blink::WebString::fromUTF8(config.id());
1092 822
1093 scoped_ptr<WebInbandTextTrackImpl> web_inband_text_track( 823 scoped_ptr<WebInbandTextTrackImpl> web_inband_text_track(
1094 new WebInbandTextTrackImpl(web_kind, web_label, web_language, web_id, 824 new WebInbandTextTrackImpl(web_kind, web_label, web_language, web_id,
1095 text_track_index_++)); 825 text_track_index_++));
1096 826
1097 scoped_ptr<media::TextTrack> text_track( 827 scoped_ptr<media::TextTrack> text_track(
1098 new TextTrackImpl(main_loop_, client_, web_inband_text_track.Pass())); 828 new TextTrackImpl(main_loop_, client_, web_inband_text_track.Pass()));
1099 829
1100 done_cb.Run(text_track.Pass()); 830 done_cb.Run(text_track.Pass());
1101 } 831 }
1102 832
1103 void WebMediaPlayerImpl::OnKeyError(const std::string& session_id,
1104 media::MediaKeys::KeyError error_code,
1105 uint32 system_code) {
1106 DCHECK(main_loop_->BelongsToCurrentThread());
1107
1108 EmeUMAHistogramEnumeration(current_key_system_, "KeyError",
1109 error_code, media::MediaKeys::kMaxKeyError);
1110
1111 unsigned short short_system_code = 0;
1112 if (system_code > std::numeric_limits<unsigned short>::max()) {
1113 LOG(WARNING) << "system_code exceeds unsigned short limit.";
1114 short_system_code = std::numeric_limits<unsigned short>::max();
1115 } else {
1116 short_system_code = static_cast<unsigned short>(system_code);
1117 }
1118
1119 client_->keyError(
1120 WebString::fromUTF8(GetPrefixedKeySystemName(current_key_system_)),
1121 WebString::fromUTF8(session_id),
1122 static_cast<blink::WebMediaPlayerClient::MediaKeyErrorCode>(error_code),
1123 short_system_code);
1124 }
1125
1126 void WebMediaPlayerImpl::OnKeyMessage(const std::string& session_id,
1127 const std::vector<uint8>& message,
1128 const GURL& destination_url) {
1129 DCHECK(main_loop_->BelongsToCurrentThread());
1130
1131 DCHECK(destination_url.is_empty() || destination_url.is_valid());
1132
1133 client_->keyMessage(
1134 WebString::fromUTF8(GetPrefixedKeySystemName(current_key_system_)),
1135 WebString::fromUTF8(session_id),
1136 message.empty() ? NULL : &message[0],
1137 message.size(),
1138 destination_url);
1139 }
1140
1141 void WebMediaPlayerImpl::DataSourceInitialized(bool success) { 833 void WebMediaPlayerImpl::DataSourceInitialized(bool success) {
1142 DCHECK(main_loop_->BelongsToCurrentThread()); 834 DCHECK(main_loop_->BelongsToCurrentThread());
1143 835
1144 if (!success) { 836 if (!success) {
1145 SetNetworkState(WebMediaPlayer::NetworkStateFormatError); 837 SetNetworkState(WebMediaPlayer::NetworkStateFormatError);
1146 return; 838 return;
1147 } 839 }
1148 840
1149 StartPipeline(); 841 StartPipeline();
1150 } 842 }
(...skipping 11 matching lines...) Expand all
1162 854
1163 void WebMediaPlayerImpl::StartPipeline() { 855 void WebMediaPlayerImpl::StartPipeline() {
1164 DCHECK(main_loop_->BelongsToCurrentThread()); 856 DCHECK(main_loop_->BelongsToCurrentThread());
1165 const CommandLine* cmd_line = CommandLine::ForCurrentProcess(); 857 const CommandLine* cmd_line = CommandLine::ForCurrentProcess();
1166 858
1167 // Keep track if this is a MSE or non-MSE playback. 859 // Keep track if this is a MSE or non-MSE playback.
1168 UMA_HISTOGRAM_BOOLEAN("Media.MSE.Playback", 860 UMA_HISTOGRAM_BOOLEAN("Media.MSE.Playback",
1169 (load_type_ == LoadTypeMediaSource)); 861 (load_type_ == LoadTypeMediaSource));
1170 862
1171 media::LogCB mse_log_cb; 863 media::LogCB mse_log_cb;
864 media::Demuxer::NeedKeyCB need_key_cb;
865
866 if (encrypted_media_support_)
867 need_key_cb = encrypted_media_support_->CreateNeedKeyCB();
1172 868
1173 // Figure out which demuxer to use. 869 // Figure out which demuxer to use.
1174 if (load_type_ != LoadTypeMediaSource) { 870 if (load_type_ != LoadTypeMediaSource) {
1175 DCHECK(!chunk_demuxer_); 871 DCHECK(!chunk_demuxer_);
1176 DCHECK(data_source_); 872 DCHECK(data_source_);
1177 873
1178 demuxer_.reset(new media::FFmpegDemuxer( 874 demuxer_.reset(new media::FFmpegDemuxer(
1179 media_loop_, data_source_.get(), 875 media_loop_, data_source_.get(),
1180 BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnNeedKey), 876 need_key_cb,
1181 media_log_)); 877 media_log_));
1182 } else { 878 } else {
1183 DCHECK(!chunk_demuxer_); 879 DCHECK(!chunk_demuxer_);
1184 DCHECK(!data_source_); 880 DCHECK(!data_source_);
1185 881
1186 mse_log_cb = base::Bind(&LogMediaSourceError, media_log_); 882 mse_log_cb = base::Bind(&LogMediaSourceError, media_log_);
1187 883
1188 chunk_demuxer_ = new media::ChunkDemuxer( 884 chunk_demuxer_ = new media::ChunkDemuxer(
1189 BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnDemuxerOpened), 885 BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnDemuxerOpened),
1190 BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::OnNeedKey), 886 need_key_cb,
1191 mse_log_cb, 887 mse_log_cb,
1192 true); 888 true);
1193 demuxer_.reset(chunk_demuxer_); 889 demuxer_.reset(chunk_demuxer_);
1194 } 890 }
1195 891
1196 scoped_ptr<media::FilterCollection> filter_collection( 892 scoped_ptr<media::FilterCollection> filter_collection(
1197 new media::FilterCollection()); 893 new media::FilterCollection());
1198 filter_collection->SetDemuxer(demuxer_.get()); 894 filter_collection->SetDemuxer(demuxer_.get());
1199 895
1200 media::SetDecryptorReadyCB set_decryptor_ready_cb = 896 media::SetDecryptorReadyCB set_decryptor_ready_cb;
1201 BIND_TO_RENDER_LOOP(&WebMediaPlayerImpl::SetDecryptorReadyCB); 897
898 if (encrypted_media_support_) {
899 set_decryptor_ready_cb =
900 encrypted_media_support_->CreateSetDecryptorReadyCB();
901 }
1202 902
1203 // Create our audio decoders and renderer. 903 // Create our audio decoders and renderer.
1204 ScopedVector<media::AudioDecoder> audio_decoders; 904 ScopedVector<media::AudioDecoder> audio_decoders;
1205 audio_decoders.push_back(new media::FFmpegAudioDecoder(media_loop_, 905 audio_decoders.push_back(new media::FFmpegAudioDecoder(media_loop_,
1206 mse_log_cb)); 906 mse_log_cb));
1207 audio_decoders.push_back(new media::OpusAudioDecoder(media_loop_)); 907 audio_decoders.push_back(new media::OpusAudioDecoder(media_loop_));
1208 908
1209 scoped_ptr<media::AudioRenderer> audio_renderer(new media::AudioRendererImpl( 909 scoped_ptr<media::AudioRenderer> audio_renderer(new media::AudioRendererImpl(
1210 media_loop_, 910 media_loop_,
1211 audio_source_provider_.get(), 911 audio_source_provider_.get(),
(...skipping 120 matching lines...) Expand 10 before | Expand all | Expand 10 after
1332 1032
1333 void WebMediaPlayerImpl::FrameReady( 1033 void WebMediaPlayerImpl::FrameReady(
1334 const scoped_refptr<media::VideoFrame>& frame) { 1034 const scoped_refptr<media::VideoFrame>& frame) {
1335 compositor_task_runner_->PostTask( 1035 compositor_task_runner_->PostTask(
1336 FROM_HERE, 1036 FROM_HERE,
1337 base::Bind(&VideoFrameCompositor::UpdateCurrentFrame, 1037 base::Bind(&VideoFrameCompositor::UpdateCurrentFrame,
1338 base::Unretained(compositor_), 1038 base::Unretained(compositor_),
1339 frame)); 1039 frame));
1340 } 1040 }
1341 1041
1342 void WebMediaPlayerImpl::SetDecryptorReadyCB(
1343 const media::DecryptorReadyCB& decryptor_ready_cb) {
1344 DCHECK(main_loop_->BelongsToCurrentThread());
1345
1346 // Cancels the previous decryptor request.
1347 if (decryptor_ready_cb.is_null()) {
1348 if (!decryptor_ready_cb_.is_null()) {
1349 base::ResetAndReturn(&decryptor_ready_cb_)
1350 .Run(NULL, base::Bind(DoNothing));
1351 }
1352 return;
1353 }
1354
1355 // TODO(xhwang): Support multiple decryptor notification request (e.g. from
1356 // video and audio). The current implementation is okay for the current
1357 // media pipeline since we initialize audio and video decoders in sequence.
1358 // But WebMediaPlayerImpl should not depend on media pipeline's implementation
1359 // detail.
1360 DCHECK(decryptor_ready_cb_.is_null());
1361
1362 // Mixed use of prefixed and unprefixed EME APIs is disallowed by Blink.
1363 DCHECK(!proxy_decryptor_ || !web_cdm_);
1364
1365 if (proxy_decryptor_) {
1366 decryptor_ready_cb.Run(proxy_decryptor_->GetDecryptor(),
1367 base::Bind(DoNothing));
1368 return;
1369 }
1370
1371 if (web_cdm_) {
1372 decryptor_ready_cb.Run(web_cdm_->GetDecryptor(), base::Bind(DoNothing));
1373 return;
1374 }
1375
1376 decryptor_ready_cb_ = decryptor_ready_cb;
1377 }
1378
1379 static void GetCurrentFrameAndSignal( 1042 static void GetCurrentFrameAndSignal(
1380 VideoFrameCompositor* compositor, 1043 VideoFrameCompositor* compositor,
1381 scoped_refptr<media::VideoFrame>* video_frame_out, 1044 scoped_refptr<media::VideoFrame>* video_frame_out,
1382 base::WaitableEvent* event) { 1045 base::WaitableEvent* event) {
1383 TRACE_EVENT0("media", "GetCurrentFrameAndSignal"); 1046 TRACE_EVENT0("media", "GetCurrentFrameAndSignal");
1384 *video_frame_out = compositor->GetCurrentFrame(); 1047 *video_frame_out = compositor->GetCurrentFrame();
1385 event->Signal(); 1048 event->Signal();
1386 } 1049 }
1387 1050
1388 scoped_refptr<media::VideoFrame> 1051 scoped_refptr<media::VideoFrame>
1389 WebMediaPlayerImpl::GetCurrentFrameFromCompositor() { 1052 WebMediaPlayerImpl::GetCurrentFrameFromCompositor() {
1390 TRACE_EVENT0("media", "WebMediaPlayerImpl::GetCurrentFrameFromCompositor"); 1053 TRACE_EVENT0("media", "WebMediaPlayerImpl::GetCurrentFrameFromCompositor");
1391 if (compositor_task_runner_->BelongsToCurrentThread()) 1054 if (compositor_task_runner_->BelongsToCurrentThread())
1392 return compositor_->GetCurrentFrame(); 1055 return compositor_->GetCurrentFrame();
1393 1056
1394 // Use a posted task and waitable event instead of a lock otherwise 1057 // Use a posted task and waitable event instead of a lock otherwise
1395 // WebGL/Canvas can see different content than what the compositor is seeing. 1058 // WebGL/Canvas can see different content than what the compositor is seeing.
1396 scoped_refptr<media::VideoFrame> video_frame; 1059 scoped_refptr<media::VideoFrame> video_frame;
1397 base::WaitableEvent event(false, false); 1060 base::WaitableEvent event(false, false);
1398 compositor_task_runner_->PostTask(FROM_HERE, 1061 compositor_task_runner_->PostTask(FROM_HERE,
1399 base::Bind(&GetCurrentFrameAndSignal, 1062 base::Bind(&GetCurrentFrameAndSignal,
1400 base::Unretained(compositor_), 1063 base::Unretained(compositor_),
1401 &video_frame, 1064 &video_frame,
1402 &event)); 1065 &event));
1403 event.Wait(); 1066 event.Wait();
1404 return video_frame; 1067 return video_frame;
1405 } 1068 }
1406 1069
1407 } // namespace content 1070 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698