1 // Copyright 2019 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 "media/renderers/win/media_foundation_source_wrapper.h"
6 
7 #include <mferror.h>
8 
9 #include "media/base/audio_decoder_config.h"
10 #include "media/base/demuxer_stream.h"
11 #include "media/base/video_decoder_config.h"
12 #include "media/base/win/mf_helpers.h"
13 
14 namespace media {
15 
16 using Microsoft::WRL::ComPtr;
17 
18 MediaFoundationSourceWrapper::MediaFoundationSourceWrapper() = default;
19 
~MediaFoundationSourceWrapper()20 MediaFoundationSourceWrapper::~MediaFoundationSourceWrapper() {
21   if (!cdm_proxy_)
22     return;
23 
24   // Notify |cdm_proxy_| of last Key IDs.
25   for (uint32_t stream_id = 0; stream_id < StreamCount(); stream_id++) {
26     HRESULT hr = cdm_proxy_->SetLastKeyId(
27         stream_id, media_streams_[stream_id]->GetLastKeyId());
28     DLOG_IF(ERROR, FAILED(hr))
29         << "Failed to notify CDM proxy of last Key IDs: " << PrintHr(hr);
30   }
31 }
32 
RuntimeClassInitialize(MediaResource * media_resource,scoped_refptr<base::SequencedTaskRunner> task_runner)33 HRESULT MediaFoundationSourceWrapper::RuntimeClassInitialize(
34     MediaResource* media_resource,
35     scoped_refptr<base::SequencedTaskRunner> task_runner) {
36   DVLOG_FUNC(1);
37 
38   if (media_resource->GetType() != MediaResource::Type::STREAM) {
39     DLOG(ERROR) << "MediaResource is not of Type STREAM";
40     return E_INVALIDARG;
41   }
42 
43   task_runner_ = task_runner;
44 
45   auto demuxer_streams = media_resource->GetAllStreams();
46 
47   int stream_id = 0;
48   for (DemuxerStream* demuxer_stream : demuxer_streams) {
49     ComPtr<MediaFoundationStreamWrapper> mf_stream;
50     RETURN_IF_FAILED(MediaFoundationStreamWrapper::Create(
51         stream_id++, this, demuxer_stream, task_runner, &mf_stream));
52     media_streams_.push_back(mf_stream);
53   }
54 
55   RETURN_IF_FAILED(MFCreateEventQueue(&mf_media_event_queue_));
56   return S_OK;
57 }
58 
DetachResource()59 void MediaFoundationSourceWrapper::DetachResource() {
60   DVLOG_FUNC(1);
61 
62   for (auto stream : media_streams_) {
63     stream->DetachDemuxerStream();
64   }
65 }
66 
GetCharacteristics(DWORD * characteristics)67 HRESULT MediaFoundationSourceWrapper::GetCharacteristics(
68     DWORD* characteristics) {
69   DVLOG_FUNC(3);
70 
71   if (state_ == State::kShutdown) {
72     DLOG(ERROR) << __func__ << ": MF_E_SHUTDOWN";
73     return MF_E_SHUTDOWN;
74   }
75   *characteristics = MFMEDIASOURCE_CAN_SEEK;
76   return S_OK;
77 }
78 
SelectDefaultStreams(const DWORD stream_desc_count,IMFPresentationDescriptor * presentation_descriptor)79 HRESULT MediaFoundationSourceWrapper::SelectDefaultStreams(
80     const DWORD stream_desc_count,
81     IMFPresentationDescriptor* presentation_descriptor) {
82   bool audio_stream_selected = false;
83   bool video_stream_selected = false;
84   for (DWORD idx = 0; idx < stream_desc_count; idx++) {
85     ComPtr<IMFStreamDescriptor> stream_descriptor;
86     BOOL selected;
87     RETURN_IF_FAILED(presentation_descriptor->GetStreamDescriptorByIndex(
88         idx, &selected, &stream_descriptor));
89     if (selected)
90       continue;
91     DWORD stream_id;
92     RETURN_IF_FAILED(stream_descriptor->GetStreamIdentifier(&stream_id));
93     if (media_streams_[stream_id]->StreamType() == DemuxerStream::Type::AUDIO &&
94         !audio_stream_selected) {
95       audio_stream_selected = true;
96       RETURN_IF_FAILED(presentation_descriptor->SelectStream(idx));
97     } else if (media_streams_[stream_id]->StreamType() ==
98                    DemuxerStream::Type::VIDEO &&
99                !video_stream_selected) {
100       video_stream_selected = true;
101       RETURN_IF_FAILED(presentation_descriptor->SelectStream(idx));
102     }
103   }
104   return S_OK;
105 }
106 
CreatePresentationDescriptor(IMFPresentationDescriptor ** presentation_descriptor_out)107 HRESULT MediaFoundationSourceWrapper::CreatePresentationDescriptor(
108     IMFPresentationDescriptor** presentation_descriptor_out) {
109   DVLOG_FUNC(2);
110 
111   if (state_ == State::kShutdown) {
112     DLOG(ERROR) << __func__ << ": MF_E_SHUTDOWN";
113     return MF_E_SHUTDOWN;
114   }
115 
116   ComPtr<IMFPresentationDescriptor> presentation_descriptor;
117   std::vector<ComPtr<IMFStreamDescriptor>> stream_descriptors;
118   for (auto stream : media_streams_) {
119     ComPtr<IMFStreamDescriptor> stream_descriptor;
120     RETURN_IF_FAILED(stream->GetStreamDescriptor(&stream_descriptor));
121     stream_descriptors.push_back(stream_descriptor);
122   }
123   const DWORD stream_desc_count = static_cast<DWORD>(stream_descriptors.size());
124   RETURN_IF_FAILED(MFCreatePresentationDescriptor(
125       stream_desc_count,
126       reinterpret_cast<IMFStreamDescriptor**>(stream_descriptors.data()),
127       &presentation_descriptor));
128   RETURN_IF_FAILED(
129       SelectDefaultStreams(stream_desc_count, presentation_descriptor.Get()));
130 
131   *presentation_descriptor_out = presentation_descriptor.Detach();
132   return S_OK;
133 }
134 
135 // https://docs.microsoft.com/en-us/windows/win32/api/mfidl/nf-mfidl-imfmediasource-start
Start(IMFPresentationDescriptor * presentation_descriptor,const GUID * guid_time_format,const PROPVARIANT * start_position)136 HRESULT MediaFoundationSourceWrapper::Start(
137     IMFPresentationDescriptor* presentation_descriptor,
138     const GUID* guid_time_format,
139     const PROPVARIANT* start_position) {
140   DVLOG_FUNC(2);
141 
142   if (state_ == State::kShutdown) {
143     DLOG(ERROR) << __func__ << ": MF_E_SHUTDOWN";
144     return MF_E_SHUTDOWN;
145   }
146 
147   bool seeked = false;
148   bool started = false;
149 
150   // - If the previous state was stopped, the source sends an MESourceStarted
151   // event.
152   // - If the previous state was started or paused and the starting position is
153   // the current position(VT_EMPTY), the source sends an MESourceStarted event.
154   // - If the previous state was started or paused, and a new starting position
155   // is specified, the source sends an MESourceSeeked event.
156   if (state_ == State::kInitialized || state_ == State::kStopped) {
157     started = true;
158   } else if (state_ == State::kPaused || state_ == State::kStarted) {
159     if (start_position->vt == VT_EMPTY)
160       started = true;
161     else
162       seeked = true;
163   }
164 
165   // - For each new stream, the source sends an MENewStream event. This event is
166   // sent for the first Start call in which the stream appears. The event data
167   // is a pointer to the stream's IMFMediaStream interface.
168   // - For each updated stream, the source sends an MEUpdatedStream event. A
169   // stream is updated if the stream already existed when Start was called (for
170   // example, if the application seeks during playback).
171   DWORD stream_desc_count = 0;
172   RETURN_IF_FAILED(
173       presentation_descriptor->GetStreamDescriptorCount(&stream_desc_count));
174 
175   for (DWORD i = 0; i < stream_desc_count; i++) {
176     ComPtr<IMFStreamDescriptor> stream_descriptor;
177     BOOL selected;
178     RETURN_IF_FAILED(presentation_descriptor->GetStreamDescriptorByIndex(
179         i, &selected, &stream_descriptor));
180 
181     DWORD stream_id;
182     RETURN_IF_FAILED(stream_descriptor->GetStreamIdentifier(&stream_id));
183 
184     if (stream_id >= media_streams_.size()) {
185       DLOG(ERROR) << "Unexpected stream id in descriptor: " << stream_id;
186       continue;
187     }
188 
189     ComPtr<MediaFoundationStreamWrapper> stream = media_streams_[stream_id];
190     stream->SetFlushed(false);
191     if (selected) {
192       MediaEventType event_type = MENewStream;
193       if (stream->IsSelected()) {
194         event_type = MEUpdatedStream;
195       }
196       ComPtr<IUnknown> unknown_stream;
197       RETURN_IF_FAILED(stream.As(&unknown_stream));
198       RETURN_IF_FAILED(mf_media_event_queue_->QueueEventParamUnk(
199           event_type, GUID_NULL, S_OK, unknown_stream.Get()));
200 
201       // - If the source sends an MESourceStarted event, each media stream
202       // sends an MEStreamStarted event. If the source sends an MESourceSeeked
203       // event, each stream sends an MEStreamSeeked event.
204       if (started) {
205         DCHECK(!seeked);
206         RETURN_IF_FAILED(stream->QueueStartedEvent(start_position));
207       } else if (seeked) {
208         RETURN_IF_FAILED(stream->QueueSeekedEvent(start_position));
209       }
210     }
211     stream->SetSelected(selected);
212   }
213 
214   if (started) {
215     DCHECK(!seeked);
216     ComPtr<IMFMediaEvent> media_event;
217     RETURN_IF_FAILED(MFCreateMediaEvent(MESourceStarted, GUID_NULL, S_OK,
218                                         start_position, &media_event));
219     RETURN_IF_FAILED(mf_media_event_queue_->QueueEvent(media_event.Get()));
220   } else if (seeked) {
221     RETURN_IF_FAILED(
222         QueueEvent(MESourceSeeked, GUID_NULL, S_OK, start_position));
223   }
224 
225   state_ = State::kStarted;
226   presentation_ended_ = false;
227   return S_OK;
228 }
229 
Stop()230 HRESULT MediaFoundationSourceWrapper::Stop() {
231   DVLOG_FUNC(2);
232 
233   if (state_ == State::kShutdown) {
234     DLOG(ERROR) << __func__ << ": MF_E_SHUTDOWN";
235     return MF_E_SHUTDOWN;
236   }
237 
238   RETURN_IF_FAILED(QueueEvent(MESourceStopped, GUID_NULL, S_OK, nullptr));
239 
240   for (auto stream : media_streams_) {
241     if (stream->IsSelected()) {
242       stream->QueueStoppedEvent();
243     }
244   }
245 
246   state_ = State::kStopped;
247   return S_OK;
248 }
249 
Pause()250 HRESULT MediaFoundationSourceWrapper::Pause() {
251   DVLOG_FUNC(2);
252 
253   if (state_ == State::kShutdown) {
254     DLOG(ERROR) << __func__ << ": MF_E_SHUTDOWN";
255     return MF_E_SHUTDOWN;
256   }
257   if (state_ != State::kStarted) {
258     DLOG(ERROR) << __func__ << ": MF_E_INVALID_STATE_TRANSITION";
259     return MF_E_INVALID_STATE_TRANSITION;
260   }
261 
262   RETURN_IF_FAILED(QueueEvent(MESourcePaused, GUID_NULL, S_OK, nullptr));
263 
264   for (auto stream : media_streams_) {
265     if (stream->IsSelected()) {
266       stream->QueuePausedEvent();
267     }
268   }
269 
270   state_ = State::kPaused;
271   return S_OK;
272 }
273 
274 // After this method is called, methods on the media source and all of its
275 // media streams return MF_E_SHUTDOWN (except for IUnknown methods).
Shutdown()276 HRESULT MediaFoundationSourceWrapper::Shutdown() {
277   DVLOG_FUNC(1);
278 
279   for (auto stream : media_streams_) {
280     stream->DetachParent();
281   }
282   state_ = State::kShutdown;
283   return S_OK;
284 }
285 
286 // TODO(frankli): In MediaFoundationSourceWrapper::XxxEvent methods below,
287 // investigate why MediaFoundationSourceWrapper is not being destructed
288 // when we return MF_E_SHUTDOWN if |state_| is State::kShutdown.
289 // https://docs.microsoft.com/en-us/windows/win32/api/mfobjects/nf-mfobjects-imfmediaeventgenerator-queueevent
290 // indicates the object can return MF_E_SHUTDOWN.
291 //
GetEvent(DWORD flags,IMFMediaEvent ** event_out)292 HRESULT MediaFoundationSourceWrapper::GetEvent(DWORD flags,
293                                                IMFMediaEvent** event_out) {
294   DVLOG_FUNC(3);
295   DCHECK(mf_media_event_queue_);
296 
297   // Not tracing hr to avoid the noise from MF_E_NO_EVENTS_AVAILABLE.
298   return mf_media_event_queue_->GetEvent(flags, event_out);
299 }
300 
BeginGetEvent(IMFAsyncCallback * callback,IUnknown * state)301 HRESULT MediaFoundationSourceWrapper::BeginGetEvent(IMFAsyncCallback* callback,
302                                                     IUnknown* state) {
303   DVLOG_FUNC(3);
304   DCHECK(mf_media_event_queue_);
305 
306   RETURN_IF_FAILED(mf_media_event_queue_->BeginGetEvent(callback, state));
307   return S_OK;
308 }
309 
EndGetEvent(IMFAsyncResult * result,IMFMediaEvent ** event_out)310 HRESULT MediaFoundationSourceWrapper::EndGetEvent(IMFAsyncResult* result,
311                                                   IMFMediaEvent** event_out) {
312   DVLOG_FUNC(3);
313   DCHECK(mf_media_event_queue_);
314 
315   RETURN_IF_FAILED(mf_media_event_queue_->EndGetEvent(result, event_out));
316   return S_OK;
317 }
318 
QueueEvent(MediaEventType type,REFGUID extended_type,HRESULT status,const PROPVARIANT * value)319 HRESULT MediaFoundationSourceWrapper::QueueEvent(MediaEventType type,
320                                                  REFGUID extended_type,
321                                                  HRESULT status,
322                                                  const PROPVARIANT* value) {
323   DVLOG_FUNC(3);
324   DCHECK(mf_media_event_queue_);
325 
326   RETURN_IF_FAILED(mf_media_event_queue_->QueueEventParamVar(
327       type, extended_type, status, value));
328   return S_OK;
329 }
330 
GetInputTrustAuthority(DWORD stream_id,REFIID riid,IUnknown ** object_out)331 HRESULT MediaFoundationSourceWrapper::GetInputTrustAuthority(
332     DWORD stream_id,
333     REFIID riid,
334     IUnknown** object_out) {
335   DVLOG_FUNC(1);
336 
337   if (stream_id >= StreamCount())
338     return E_INVALIDARG;
339 
340   if (!cdm_proxy_) {
341     DVLOG_FUNC(1) << "MF_E_NOT_PROTECTED";
342     return MF_E_NOT_PROTECTED;
343   }
344 
345   if (!media_streams_[stream_id]->IsEncrypted()) {
346     DVLOG_FUNC(1) << "Unprotected stream; stream_id=" << stream_id;
347 
348     return MF_E_NOT_PROTECTED;
349   }
350 
351   // Use |nullptr| for content init_data and |0| for its size.
352   RETURN_IF_FAILED(cdm_proxy_->GetInputTrustAuthority(
353       stream_id, StreamCount(), nullptr, 0, riid, object_out));
354   return S_OK;
355 }
356 
GetService(REFGUID guid_service,REFIID riid,LPVOID * result)357 HRESULT MediaFoundationSourceWrapper::GetService(REFGUID guid_service,
358                                                  REFIID riid,
359                                                  LPVOID* result) {
360   DVLOG_FUNC(3);
361   DCHECK(result);
362 
363   if (!IsEqualGUID(guid_service, MF_RATE_CONTROL_SERVICE))
364     return MF_E_UNSUPPORTED_SERVICE;
365   return QueryInterface(riid, result);
366 }
367 
GetSlowestRate(MFRATE_DIRECTION direction,BOOL supports_thinning,float * rate)368 HRESULT MediaFoundationSourceWrapper::GetSlowestRate(MFRATE_DIRECTION direction,
369                                                      BOOL supports_thinning,
370                                                      float* rate) {
371   DVLOG_FUNC(3);
372   DCHECK(rate);
373 
374   if (direction == MFRATE_REVERSE) {
375     return MF_E_REVERSE_UNSUPPORTED;
376   }
377   *rate = 0.0f;
378   return state_ == State::kShutdown ? MF_E_SHUTDOWN : S_OK;
379 }
380 
GetFastestRate(MFRATE_DIRECTION direction,BOOL supports_thinning,float * rate)381 HRESULT MediaFoundationSourceWrapper::GetFastestRate(MFRATE_DIRECTION direction,
382                                                      BOOL supports_thinning,
383                                                      float* rate) {
384   DVLOG_FUNC(3);
385   DCHECK(rate);
386 
387   if (direction == MFRATE_REVERSE) {
388     return MF_E_REVERSE_UNSUPPORTED;
389   }
390   *rate = 0.0f;
391   HRESULT hr = MF_E_SHUTDOWN;
392   if (state_ != State::kShutdown) {
393     *rate = 8.0f;
394     hr = S_OK;
395   }
396   return hr;
397 }
398 
IsRateSupported(BOOL supports_thinning,float new_rate,float * supported_rate)399 HRESULT MediaFoundationSourceWrapper::IsRateSupported(BOOL supports_thinning,
400                                                       float new_rate,
401                                                       float* supported_rate) {
402   DVLOG_FUNC(2) << "new_rate=" << new_rate;
403 
404   if (state_ == State::kShutdown)
405     return MF_E_SHUTDOWN;
406 
407   if (supported_rate)
408     *supported_rate = 0.0f;
409 
410   MFRATE_DIRECTION direction =
411       (new_rate >= 0) ? MFRATE_FORWARD : MFRATE_REVERSE;
412   float fastest_rate = 0.0f;
413   float slowest_rate = 0.0f;
414 
415   GetFastestRate(direction, supports_thinning, &fastest_rate);
416   GetSlowestRate(direction, supports_thinning, &slowest_rate);
417 
418   HRESULT hr;
419   if (supports_thinning) {
420     // We do not support thinning right now. If Thin is supported, this
421     // MediaSource is expected to drop all samples except ones marked as
422     // MFSampleExtension_CleanPoint
423     hr = MF_E_THINNING_UNSUPPORTED;
424   } else if (new_rate < slowest_rate) {
425     hr = MF_E_REVERSE_UNSUPPORTED;
426   } else if (new_rate > fastest_rate) {
427     hr = MF_E_UNSUPPORTED_RATE;
428   } else {
429     // Supported
430     hr = S_OK;
431     if (supported_rate) {
432       *supported_rate = new_rate;
433     }
434   }
435 
436   DVLOG_FUNC(2) << PrintHr(hr);
437   return hr;
438 }
439 
SetRate(BOOL supports_thinning,float rate)440 HRESULT MediaFoundationSourceWrapper::SetRate(BOOL supports_thinning,
441                                               float rate) {
442   DVLOG_FUNC(2);
443 
444   if (state_ == State::kShutdown)
445     return MF_E_SHUTDOWN;
446 
447   HRESULT hr = IsRateSupported(supports_thinning, rate, &current_rate_);
448   if (SUCCEEDED(hr)) {
449     PROPVARIANT varRate;
450     varRate.vt = VT_R4;
451     varRate.fltVal = current_rate_;
452     hr = QueueEvent(MESourceRateChanged, GUID_NULL, S_OK, &varRate);
453   }
454   return hr;
455 }
456 
GetRate(BOOL * supports_thinning,float * rate)457 HRESULT MediaFoundationSourceWrapper::GetRate(BOOL* supports_thinning,
458                                               float* rate) {
459   DVLOG_FUNC(2);
460 
461   *supports_thinning = FALSE;
462   *rate = 0.0f;
463   HRESULT hr = MF_E_SHUTDOWN;
464   if (state_ != State::kShutdown) {
465     hr = S_OK;
466     *rate = current_rate_;
467   }
468   return hr;
469 }
470 
StreamCount() const471 uint32_t MediaFoundationSourceWrapper::StreamCount() const {
472   return media_streams_.size();
473 }
474 
CheckForEndOfPresentation()475 void MediaFoundationSourceWrapper::CheckForEndOfPresentation() {
476   DVLOG_FUNC(2);
477   DCHECK(task_runner_->RunsTasksInCurrentSequence());
478 
479   if (presentation_ended_) {
480     return;
481   }
482 
483   bool presentation_ended = true;
484   for (auto mf_stream : media_streams_) {
485     if (!mf_stream->HasEnded()) {
486       presentation_ended = false;
487       break;
488     }
489   }
490   presentation_ended_ = presentation_ended;
491   if (presentation_ended_) {
492     HRESULT hr = QueueEvent(MEEndOfPresentation, GUID_NULL, S_OK, nullptr);
493     DLOG_IF(ERROR, FAILED(hr))
494         << "Failed to notify end of presentation: " << PrintHr(hr);
495   }
496 }
497 
HasEncryptedStream() const498 bool MediaFoundationSourceWrapper::HasEncryptedStream() const {
499   DCHECK(task_runner_->RunsTasksInCurrentSequence());
500 
501   for (auto mf_stream : media_streams_) {
502     if (mf_stream->IsEncrypted()) {
503       return true;
504     }
505   }
506   return false;
507 }
508 
SetCdmProxy(IMFCdmProxy * cdm_proxy)509 void MediaFoundationSourceWrapper::SetCdmProxy(IMFCdmProxy* cdm_proxy) {
510   DVLOG_FUNC(2);
511   DCHECK(task_runner_->RunsTasksInCurrentSequence());
512 
513   // cdm_proxy_ should never change.
514   DCHECK(!cdm_proxy_);
515   cdm_proxy_ = cdm_proxy;
516 
517   HRESULT hr = cdm_proxy_->RefreshTrustedInput();
518   DLOG_IF(ERROR, FAILED(hr))
519       << "Failed to refresh trusted input: " << PrintHr(hr);
520 }
521 
SetVideoStreamEnabled(bool enabled)522 bool MediaFoundationSourceWrapper::SetVideoStreamEnabled(bool enabled) {
523   DVLOG_FUNC(2) << "enabled=" << enabled;
524   DCHECK(task_runner_->RunsTasksInCurrentSequence());
525 
526   if (enabled == video_stream_enabled_)
527     return false;
528 
529   video_stream_enabled_ = enabled;
530 
531   for (auto stream : media_streams_) {
532     if (stream->IsSelected() &&
533         stream->StreamType() == DemuxerStream::Type::VIDEO) {
534       stream->SetEnabled(enabled);
535       if (enabled && stream->HasEnded()) {
536         // Need to restart (pause+play) if we're re-enabling a video stream
537         // which has previously reached EOS.
538         return true;
539       }
540     }
541   }
542   return false;
543 }
544 
FlushStreams()545 void MediaFoundationSourceWrapper::FlushStreams() {
546   DVLOG_FUNC(2);
547   DCHECK(task_runner_->RunsTasksInCurrentSequence());
548 
549   for (auto stream : media_streams_) {
550     stream->SetFlushed(true);
551   }
552 }
553 
554 }  // namespace media
555