1 // Copyright (c) Microsoft. All rights reserved.
2 //
3 // The MIT License (MIT)
4 //
5 // Permission is hereby granted, free of charge, to any person obtaining a copy
6 // of this software and associated documentation files(the "Software"), to deal
7 // in the Software without restriction, including without limitation the rights
8 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 // copies of the Software, and to permit persons to whom the Software is
10 // furnished to do so, subject to the following conditions :
11 //
12 // The above copyright notice and this permission notice shall be included in
13 // all copies or substantial portions of the Software.
14 //
15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
18 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 // THE SOFTWARE.
22 
23 #include "MediaStreamSink.hpp"
24 #include "MFIncludes.hpp"
25 
26 using namespace Media;
27 using namespace Microsoft::WRL;
28 using namespace Platform;
29 using namespace Windows::Foundation;
30 
31 MediaStreamSink::MediaStreamSink(
32     __in const MW::ComPtr<IMFMediaSink>& sink,
33     __in DWORD id,
34     __in const MW::ComPtr<IMFMediaType>& mt,
35     __in MediaSampleHandler^ sampleHandler
36     )
37     : _shutdown(false)
38     , _id(-1)
39     , _width(0)
40     , _height(0)
41 {
42     CHK(MFCreateEventQueue(&_eventQueue));
43     CHK(MFCreateMediaType(&_curMT));
44 
45     _UpdateMediaType(mt);
46 
47     _sink = sink;
48     _id = id;
49     _sampleHandler = sampleHandler;
50 }
51 
GetMediaSink(__deref_out IMFMediaSink ** sink)52 HRESULT MediaStreamSink::GetMediaSink(__deref_out IMFMediaSink **sink)
53 {
54     return ExceptionBoundary([this, sink]()
55     {
56         auto lock = _lock.LockExclusive();
57 
58         CHKNULL(sink);
59         *sink = nullptr;
60 
61         _VerifyNotShutdown();
62 
63         CHK(_sink.CopyTo(sink));
64     });
65 }
66 
GetIdentifier(__out DWORD * identifier)67 HRESULT MediaStreamSink::GetIdentifier(__out DWORD *identifier)
68 {
69     return ExceptionBoundary([this, identifier]()
70     {
71         auto lock = _lock.LockExclusive();
72 
73         CHKNULL(identifier);
74 
75         _VerifyNotShutdown();
76 
77         *identifier = _id;
78     });
79 }
80 
GetMediaTypeHandler(__deref_out IMFMediaTypeHandler ** handler)81 HRESULT MediaStreamSink::GetMediaTypeHandler(__deref_out IMFMediaTypeHandler **handler)
82 {
83     return ExceptionBoundary([this, handler]()
84     {
85         auto lock = _lock.LockExclusive();
86 
87         CHKNULL(handler);
88         *handler = nullptr;
89 
90         _VerifyNotShutdown();
91 
92         *handler = this;
93         this->AddRef();
94 
95     });
96 }
97 
RequestSample()98 void MediaStreamSink::RequestSample()
99 {
100     auto lock = _lock.LockExclusive();
101 
102     _VerifyNotShutdown();
103 
104     CHK(_eventQueue->QueueEventParamVar(MEStreamSinkRequestSample, GUID_NULL, S_OK, nullptr));
105 }
106 
ProcessSample(__in_opt IMFSample * sample)107 HRESULT MediaStreamSink::ProcessSample(__in_opt IMFSample *sample)
108 {
109     return ExceptionBoundary([this, sample]()
110     {
111         MediaSampleHandler^ sampleHandler;
112         auto mediaSample = ref new MediaSample();
113 
114         {
115             auto lock = _lock.LockExclusive();
116 
117             _VerifyNotShutdown();
118 
119             if (sample == nullptr)
120             {
121                 return;
122             }
123 
124             mediaSample->Sample = sample;
125             sampleHandler = _sampleHandler;
126         }
127 
128         // Call back without the lock taken to avoid deadlocks
129         sampleHandler(mediaSample);
130     });
131 }
132 
PlaceMarker(__in MFSTREAMSINK_MARKER_TYPE,__in const PROPVARIANT *,__in const PROPVARIANT * contextValue)133 HRESULT MediaStreamSink::PlaceMarker(__in MFSTREAMSINK_MARKER_TYPE /*markerType*/, __in const PROPVARIANT * /*markerValue*/, __in const PROPVARIANT * contextValue)
134 {
135     return ExceptionBoundary([this, contextValue]()
136     {
137         auto lock = _lock.LockExclusive();
138         CHKNULL(contextValue);
139 
140         _VerifyNotShutdown();
141 
142         CHK(_eventQueue->QueueEventParamVar(MEStreamSinkMarker, GUID_NULL, S_OK, contextValue));
143     });
144 }
145 
Flush()146 HRESULT MediaStreamSink::Flush()
147 {
148     return ExceptionBoundary([this]()
149     {
150         auto lock = _lock.LockExclusive();
151 
152         _VerifyNotShutdown();
153     });
154 }
155 
GetEvent(__in DWORD flags,__deref_out IMFMediaEvent ** event)156 HRESULT MediaStreamSink::GetEvent(__in DWORD flags, __deref_out IMFMediaEvent **event)
157 {
158     return ExceptionBoundary([this, flags, event]()
159     {
160         CHKNULL(event);
161         *event = nullptr;
162 
163         ComPtr<IMFMediaEventQueue> eventQueue;
164 
165         {
166             auto lock = _lock.LockExclusive();
167 
168             _VerifyNotShutdown();
169 
170             eventQueue = _eventQueue;
171         }
172 
173         // May block for a while
174         CHK(eventQueue->GetEvent(flags, event));
175     });
176 }
177 
BeginGetEvent(__in IMFAsyncCallback * callback,__in_opt IUnknown * state)178 HRESULT MediaStreamSink::BeginGetEvent(__in IMFAsyncCallback *callback, __in_opt IUnknown *state)
179 {
180     return ExceptionBoundary([this, callback, state]()
181     {
182         auto lock = _lock.LockExclusive();
183 
184         _VerifyNotShutdown();
185 
186         CHK(_eventQueue->BeginGetEvent(callback, state));
187     });
188 }
189 
190 
EndGetEvent(__in IMFAsyncResult * result,__deref_out IMFMediaEvent ** event)191 HRESULT MediaStreamSink::EndGetEvent(__in IMFAsyncResult *result, __deref_out IMFMediaEvent **event)
192 {
193     return ExceptionBoundary([this, result, event]()
194     {
195         auto lock = _lock.LockExclusive();
196 
197         CHKNULL(event);
198         *event = nullptr;
199 
200         _VerifyNotShutdown();
201 
202         CHK(_eventQueue->EndGetEvent(result, event));
203     });
204 }
205 
QueueEvent(__in MediaEventType met,__in REFGUID extendedType,__in HRESULT status,__in_opt const PROPVARIANT * value)206 HRESULT MediaStreamSink::QueueEvent(
207     __in MediaEventType met,
208     __in REFGUID extendedType,
209     __in HRESULT status,
210     __in_opt const PROPVARIANT *value
211     )
212 {
213     return ExceptionBoundary([this, met, extendedType, status, value]()
214     {
215         auto lock = _lock.LockExclusive();
216 
217         _VerifyNotShutdown();
218 
219         CHK(_eventQueue->QueueEventParamVar(met, extendedType, status, value));
220     });
221 }
222 
IsMediaTypeSupported(__in IMFMediaType * mediaType,__deref_out_opt IMFMediaType ** closestMediaType)223 HRESULT MediaStreamSink::IsMediaTypeSupported(__in IMFMediaType *mediaType, __deref_out_opt  IMFMediaType **closestMediaType)
224 {
225     bool supported = false;
226 
227     HRESULT hr = ExceptionBoundary([this, mediaType, closestMediaType, &supported]()
228     {
229         auto lock = _lock.LockExclusive();
230 
231         if (closestMediaType != nullptr)
232         {
233             *closestMediaType = nullptr;
234         }
235 
236         CHKNULL(mediaType);
237 
238         _VerifyNotShutdown();
239 
240         supported = _IsMediaTypeSupported(mediaType);
241     });
242 
243     // Avoid throwing an exception to return MF_E_INVALIDMEDIATYPE as this is not a exceptional case
244     return FAILED(hr) ? hr : supported ? S_OK : MF_E_INVALIDMEDIATYPE;
245 }
246 
GetMediaTypeCount(__out DWORD * typeCount)247 HRESULT MediaStreamSink::GetMediaTypeCount(__out DWORD *typeCount)
248 {
249     return ExceptionBoundary([this, typeCount]()
250     {
251         auto lock = _lock.LockExclusive();
252 
253         CHKNULL(typeCount);
254 
255         _VerifyNotShutdown();
256 
257         // No media type provided by default (app needs to specify it)
258         *typeCount = 0;
259     });
260 }
261 
GetMediaTypeByIndex(__in DWORD,__deref_out IMFMediaType ** mediaType)262 HRESULT MediaStreamSink::GetMediaTypeByIndex(__in DWORD /*index*/, __deref_out  IMFMediaType **mediaType)
263 {
264     HRESULT hr = ExceptionBoundary([this, mediaType]()
265     {
266         auto lock = _lock.LockExclusive();
267 
268         CHKNULL(mediaType);
269         *mediaType = nullptr;
270 
271         _VerifyNotShutdown();
272     });
273 
274     // Avoid throwing an exception to return MF_E_NO_MORE_TYPES as this is not a exceptional case
275     return FAILED(hr) ? hr : MF_E_NO_MORE_TYPES;
276 }
277 
SetCurrentMediaType(__in IMFMediaType * mediaType)278 HRESULT MediaStreamSink::SetCurrentMediaType(__in IMFMediaType *mediaType)
279 {
280     return ExceptionBoundary([this, mediaType]()
281     {
282         auto lock = _lock.LockExclusive();
283 
284         CHKNULL(mediaType);
285 
286         _VerifyNotShutdown();
287 
288         if (!_IsMediaTypeSupported(mediaType))
289         {
290             CHK(MF_E_INVALIDMEDIATYPE);
291         }
292 
293         _UpdateMediaType(mediaType);
294     });
295 }
296 
GetCurrentMediaType(__deref_out_opt IMFMediaType ** mediaType)297 HRESULT MediaStreamSink::GetCurrentMediaType(__deref_out_opt IMFMediaType **mediaType)
298 {
299     return ExceptionBoundary([this, mediaType]()
300     {
301         auto lock = _lock.LockExclusive();
302 
303         CHKNULL(mediaType);
304         *mediaType = nullptr;
305 
306         _VerifyNotShutdown();
307 
308         ComPtr<IMFMediaType> mt;
309         CHK(MFCreateMediaType(&mt));
310         CHK(_curMT->CopyAllItems(mt.Get()));
311         *mediaType = mt.Detach();
312     });
313 }
314 
GetMajorType(__out GUID * majorType)315 HRESULT MediaStreamSink::GetMajorType(__out GUID *majorType)
316 {
317     return ExceptionBoundary([this, majorType]()
318     {
319         auto lock = _lock.LockExclusive();
320 
321         CHKNULL(majorType);
322 
323         _VerifyNotShutdown();
324 
325         *majorType = _majorType;
326     });
327 }
328 
InternalSetCurrentMediaType(__in const ComPtr<IMFMediaType> & mediaType)329 void MediaStreamSink::InternalSetCurrentMediaType(__in const ComPtr<IMFMediaType>& mediaType)
330 {
331     auto lock = _lock.LockExclusive();
332 
333     CHKNULL(mediaType);
334 
335     _VerifyNotShutdown();
336 
337     _UpdateMediaType(mediaType);
338 }
339 
Shutdown()340 void MediaStreamSink::Shutdown()
341 {
342     auto lock = _lock.LockExclusive();
343 
344     if (_shutdown)
345     {
346         return;
347     }
348     _shutdown = true;
349 
350     (void)_eventQueue->Shutdown();
351     _eventQueue = nullptr;
352 
353     _curMT = nullptr;
354     _sink = nullptr;
355     _sampleHandler = nullptr;
356 }
357 
_IsMediaTypeSupported(__in const ComPtr<IMFMediaType> & mt) const358 bool MediaStreamSink::_IsMediaTypeSupported(__in const ComPtr<IMFMediaType>& mt) const
359 {
360     GUID majorType;
361     GUID subType;
362     if (SUCCEEDED(mt->GetGUID(MF_MT_MAJOR_TYPE, &majorType)) &&
363         SUCCEEDED(mt->GetGUID(MF_MT_SUBTYPE, &subType)) &&
364         (majorType == _majorType) &&
365         (subType == _subType))
366     {
367         return true;
368     }
369 
370     return false;
371 }
372 
_UpdateMediaType(__in const ComPtr<IMFMediaType> & mt)373 void MediaStreamSink::_UpdateMediaType(__in const ComPtr<IMFMediaType>& mt)
374 {
375     CHK(mt->GetGUID(MF_MT_MAJOR_TYPE, &_majorType));
376     CHK(mt->GetGUID(MF_MT_SUBTYPE, &_subType));
377 
378     if (_majorType == MFMediaType_Video)
379     {
380         CHK(MFGetAttributeSize(mt.Get(), MF_MT_FRAME_SIZE, &_width, &_height));
381     }
382 
383     CHK(mt->CopyAllItems(_curMT.Get()));
384 }