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 #pragma once
24 
25 #include "MediaStreamSink.hpp"
26 #include "MFIncludes.hpp"
27 
28 namespace Media {
29 
30 const unsigned int c_audioStreamSinkId = 0;
31 const unsigned int c_videoStreamSinkId = 1;
32 
33 class MediaSink WrlSealed
34     : public MW::RuntimeClass<
35     MW::RuntimeClassFlags<
36     MW::RuntimeClassType::WinRtClassicComMix>
37     , AWM::IMediaExtension
38     , IMFMediaSink
39     , IMFClockStateSink
40     , MW::FtmBase
41     >
42 {
43     InspectableClass(L"MediaSink", BaseTrust)
44 
45 public:
46 
47     MediaSink(
48         _In_opt_ WMMp::AudioEncodingProperties^ audioProps,
49         _In_opt_ WMMp::VideoEncodingProperties^ videoProps,
50         _In_opt_ MediaSampleHandler^ audioSampleHandler,
51         _In_opt_ MediaSampleHandler^ videoSampleHandler
52         )
53         : _shutdown(false)
54     {
55         MW::ComPtr<IMFMediaType> audioMT;
56         if (audioProps != nullptr)
57         {
58             CHK(MFCreateMediaTypeFromProperties(As<IUnknown>(audioProps).Get(), &audioMT));
59             _audioStreamSink = MW::Make<MediaStreamSink>(
60                 this,
61                 c_audioStreamSinkId,
62                 audioMT,
63                 audioSampleHandler
64                 );
65         }
66 
67         MW::ComPtr<IMFMediaType> videoMT;
68         if (videoProps != nullptr)
69         {
70             CHK(MFCreateMediaTypeFromProperties(As<IUnknown>(videoProps).Get(), &videoMT));
71             _videoStreamSink = MW::Make<MediaStreamSink>(
72                 this,
73                 c_videoStreamSinkId,
74                 videoMT,
75                 videoSampleHandler
76                 );
77         }
78     }
79 
RequestAudioSample()80     void RequestAudioSample()
81     {
82         auto lock = _lock.LockExclusive();
83 
84         _VerifyNotShutdown();
85 
86         _audioStreamSink->RequestSample();
87     }
88 
RequestVideoSample()89     void RequestVideoSample()
90     {
91         auto lock = _lock.LockExclusive();
92 
93         _VerifyNotShutdown();
94 
95         _videoStreamSink->RequestSample();
96     }
97 
SetCurrentAudioMediaType(_In_ IMFMediaType * mt)98     void SetCurrentAudioMediaType(_In_ IMFMediaType* mt)
99     {
100         auto lock = _lock.LockExclusive();
101 
102         _VerifyNotShutdown();
103 
104         _audioStreamSink->InternalSetCurrentMediaType(mt);
105     }
106 
SetCurrentVideoMediaType(_In_ IMFMediaType * mt)107     void SetCurrentVideoMediaType(_In_ IMFMediaType* mt)
108     {
109         auto lock = _lock.LockExclusive();
110 
111         _VerifyNotShutdown();
112 
113         _videoStreamSink->InternalSetCurrentMediaType(mt);
114     }
115 
116     //
117     // IMediaExtension
118     //
119 
SetProperties(_In_ AWFC::IPropertySet *)120     IFACEMETHODIMP SetProperties(_In_ AWFC::IPropertySet * /*configuration*/)
121     {
122         return ExceptionBoundary([this]()
123         {
124             auto lock = _lock.LockExclusive();
125 
126             _VerifyNotShutdown();
127         });
128     }
129 
130     //
131     // IMFMediaSink
132     //
133 
GetCharacteristics(_Out_ DWORD * characteristics)134     IFACEMETHODIMP GetCharacteristics(_Out_ DWORD *characteristics)
135     {
136         return ExceptionBoundary([this, characteristics]()
137         {
138             _VerifyNotShutdown();
139 
140             CHKNULL(characteristics);
141             *characteristics = MEDIASINK_RATELESS | MEDIASINK_FIXED_STREAMS;
142         });
143     }
144 
AddStreamSink(DWORD,_In_ IMFMediaType *,_COM_Outptr_ IMFStreamSink ** streamSink)145     IFACEMETHODIMP AddStreamSink(
146         DWORD /*streamSinkIdentifier*/,
147         _In_ IMFMediaType * /*mediaType*/,
148         _COM_Outptr_ IMFStreamSink **streamSink
149         )
150     {
151         return ExceptionBoundary([this, streamSink]()
152         {
153             _VerifyNotShutdown();
154 
155             CHKNULL(streamSink);
156             *streamSink = nullptr;
157 
158             CHK(MF_E_STREAMSINKS_FIXED);
159         });
160     }
161 
RemoveStreamSink(DWORD)162     IFACEMETHODIMP RemoveStreamSink(DWORD /*streamSinkIdentifier*/)
163     {
164         return ExceptionBoundary([this]()
165         {
166             _VerifyNotShutdown();
167 
168             CHK(MF_E_STREAMSINKS_FIXED);
169         });
170     }
171 
GetStreamSinkCount(_Out_ DWORD * streamSinkCount)172     IFACEMETHODIMP GetStreamSinkCount(_Out_ DWORD *streamSinkCount)
173     {
174         return ExceptionBoundary([this, streamSinkCount]()
175         {
176             CHKNULL(streamSinkCount);
177 
178             _VerifyNotShutdown();
179 
180             *streamSinkCount = (_audioStreamSink != nullptr) + (_videoStreamSink != nullptr);
181         });
182     }
183 
GetStreamSinkByIndex(DWORD index,_COM_Outptr_ IMFStreamSink ** streamSink)184     IFACEMETHODIMP GetStreamSinkByIndex(DWORD index, _COM_Outptr_ IMFStreamSink **streamSink)
185     {
186         return ExceptionBoundary([this, index, streamSink]()
187         {
188             auto lock = _lock.LockExclusive();
189 
190             CHKNULL(streamSink);
191             *streamSink = nullptr;
192 
193             _VerifyNotShutdown();
194 
195             switch (index)
196             {
197             case 0:
198                 if (_audioStreamSink != nullptr)
199                 {
200                     CHK(_audioStreamSink.CopyTo(streamSink));
201                 }
202                 else
203                 {
204                     CHK(_videoStreamSink.CopyTo(streamSink));
205                 }
206                 break;
207 
208             case 1:
209                 if ((_audioStreamSink != nullptr) && (_videoStreamSink != nullptr))
210                 {
211                     CHK(_videoStreamSink.CopyTo(streamSink));
212                 }
213                 else
214                 {
215                     CHK(E_INVALIDARG);
216                 }
217                 break;
218 
219             default:
220                 CHK(E_INVALIDARG);
221             }
222         });
223     }
224 
GetStreamSinkById(DWORD identifier,_COM_Outptr_ IMFStreamSink ** streamSink)225     IFACEMETHODIMP GetStreamSinkById(DWORD identifier, _COM_Outptr_ IMFStreamSink **streamSink)
226     {
227         return ExceptionBoundary([this, identifier, streamSink]()
228         {
229             auto lock = _lock.LockExclusive();
230 
231             CHKNULL(streamSink);
232             *streamSink = nullptr;
233 
234             _VerifyNotShutdown();
235 
236             if ((identifier == 0) && (_audioStreamSink != nullptr))
237             {
238                 CHK(_audioStreamSink.CopyTo(streamSink));
239             }
240             else if ((identifier == 1) && (_videoStreamSink != nullptr))
241             {
242                 CHK(_videoStreamSink.CopyTo(streamSink));
243             }
244             else
245             {
246                 CHK(E_INVALIDARG);
247             }
248         });
249     }
250 
SetPresentationClock(_In_ IMFPresentationClock * clock)251     IFACEMETHODIMP SetPresentationClock(_In_ IMFPresentationClock *clock)
252     {
253         return ExceptionBoundary([this, clock]()
254         {
255             auto lock = _lock.LockExclusive();
256 
257             _VerifyNotShutdown();
258 
259             if (_clock != nullptr)
260             {
261                 CHK(_clock->RemoveClockStateSink(this));
262                 _clock = nullptr;
263             }
264 
265             if (clock != nullptr)
266             {
267                 CHK(clock->AddClockStateSink(this));
268                 _clock = clock;
269             }
270         });
271     }
272 
GetPresentationClock(_COM_Outptr_ IMFPresentationClock ** clock)273     IFACEMETHODIMP GetPresentationClock(_COM_Outptr_ IMFPresentationClock **clock)
274     {
275         return ExceptionBoundary([this, clock]()
276         {
277             auto lock = _lock.LockExclusive();
278 
279             CHKNULL(clock);
280             *clock = nullptr;
281 
282             _VerifyNotShutdown();
283 
284             if (_clock != nullptr)
285             {
286                 CHK(_clock.CopyTo(clock))
287             }
288         });
289     }
290 
Shutdown()291     IFACEMETHODIMP Shutdown()
292     {
293         return ExceptionBoundary([this]()
294         {
295             auto lock = _lock.LockExclusive();
296 
297             if (_shutdown)
298             {
299                 return;
300             }
301             _shutdown = true;
302 
303             if (_audioStreamSink != nullptr)
304             {
305                 _audioStreamSink->Shutdown();
306                 _audioStreamSink = nullptr;
307             }
308 
309             if (_videoStreamSink != nullptr)
310             {
311                 _videoStreamSink->Shutdown();
312                 _videoStreamSink = nullptr;
313             }
314 
315             if (_clock != nullptr)
316             {
317                 (void)_clock->RemoveClockStateSink(this);
318                 _clock = nullptr;
319             }
320         });
321     }
322 
323     //
324     // IMFClockStateSink methods
325     //
326 
OnClockStart(MFTIME,LONGLONG)327     IFACEMETHODIMP OnClockStart(MFTIME /*hnsSystemTime*/, LONGLONG /*llClockStartOffset*/)
328     {
329         return ExceptionBoundary([this]()
330         {
331             auto lock = _lock.LockExclusive();
332 
333             _VerifyNotShutdown();
334         });
335     }
336 
OnClockStop(MFTIME)337     IFACEMETHODIMP OnClockStop(MFTIME /*hnsSystemTime*/)
338     {
339         return ExceptionBoundary([this]()
340         {
341             auto lock = _lock.LockExclusive();
342 
343             _VerifyNotShutdown();
344         });
345     }
346 
OnClockPause(MFTIME)347     IFACEMETHODIMP OnClockPause(MFTIME /*hnsSystemTime*/)
348     {
349         return ExceptionBoundary([this]()
350         {
351             auto lock = _lock.LockExclusive();
352 
353             _VerifyNotShutdown();
354         });
355     }
356 
OnClockRestart(MFTIME)357     IFACEMETHODIMP OnClockRestart(MFTIME /*hnsSystemTime*/)
358     {
359         return ExceptionBoundary([this]()
360         {
361             auto lock = _lock.LockExclusive();
362 
363             _VerifyNotShutdown();
364         });
365     }
366 
OnClockSetRate(MFTIME,float)367     IFACEMETHODIMP OnClockSetRate(MFTIME /*hnsSystemTime*/, float /*flRate*/)
368     {
369         return ExceptionBoundary([this]()
370         {
371             auto lock = _lock.LockExclusive();
372 
373             _VerifyNotShutdown();
374         });
375     }
376 
377 private:
378 
379     bool _shutdown;
380 
_VerifyNotShutdown()381     void _VerifyNotShutdown()
382     {
383         if (_shutdown)
384         {
385             CHK(MF_E_SHUTDOWN);
386         }
387     }
388 
389     MW::ComPtr<MediaStreamSink> _audioStreamSink;
390     MW::ComPtr<MediaStreamSink> _videoStreamSink;
391     MW::ComPtr<IMFPresentationClock> _clock;
392 
393     MWW::SRWLock _lock;
394 };
395 
396 }