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, ¤t_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