source: trunk/Utilities/MediaFoundation/SimultaneousCapture/Module.h @ 669

Last change on this file since 669 was 669, checked in by roman, 8 years ago

VS2015 upgrade

File size: 47.5 KB
Line 
1////////////////////////////////////////////////////////////
2// Copyright (C) Roman Ryltsov, 2016
3// Created by Roman Ryltsov roman@alax.info
4//
5// A permission to use the source code is granted as long as reference to
6// source website http://alax.info is retained.
7
8#pragma once
9
10#include "rodshow.h"
11#include "romf.h"
12
13////////////////////////////////////////////////////////////
14// CModule
15
16class CModule :
17        public CAtlExeModuleT<CModule>
18{
19public:
20
21        ////////////////////////////////////////////////////////
22        // CCommandLineArguments
23
24        class CCommandLineArguments
25        {
26        public:
27
28                //////////////////////////////////////////////////////////
29                // CArgument
30
31                class CArgument
32                {
33                public:
34                        BOOL m_bSwitch;
35                        CString m_sSwitchName;
36                        CString m_sSwitchValue;
37                        BOOL m_bIntegerSwitchValueAvailable;
38                        INT m_nIntegerSwitchValue;
39                        CString m_sValue;
40
41                public:
42                // CArgument
43                        CArgument() :
44                                m_bSwitch(FALSE),
45                                m_bIntegerSwitchValueAvailable(FALSE)
46                        {
47                        }
48                        VOID Initialize()
49                        {
50                                m_bSwitch = FALSE;
51                                m_sSwitchName.Empty();;
52                                m_sSwitchValue.Empty();;
53                                m_bIntegerSwitchValueAvailable = FALSE;
54                                m_sValue.Empty();
55                        }
56                };
57
58        private:
59                SIZE_T m_argc;
60                TCHAR** m_argv;
61                SIZE_T m_nIndex;
62
63        public:
64        // CCommandLineArguments
65                CCommandLineArguments(SIZE_T argc, TCHAR* argv[]) :
66                        m_argc(argc),
67                        m_argv(argv),
68                        m_nIndex(1)
69                {
70                }
71                CCommandLineArguments(LPCWSTR pszCommandLine)
72                {
73                        INT nArgumentCount = 0;
74                        LPWSTR* pszArguments = CommandLineToArgvW(pszCommandLine, &nArgumentCount);
75                        m_argc = nArgumentCount;
76                        m_argv = pszArguments;
77                        m_nIndex = 1;
78                }
79                BOOL Next(CArgument& Argument)
80                {
81                        if(m_nIndex >= m_argc)
82                                return FALSE;
83                        CString sArgument = m_argv[m_nIndex++];
84                        _A(!sArgument.IsEmpty());
85                        Argument.Initialize();
86                        if(_tcschr(_T("-/"), sArgument[0]))
87                        {
88                                Argument.m_bSwitch = TRUE;
89                                sArgument.Delete(0);
90                                const INT nSeparatorPosition = sArgument.Find(_T(':'));
91                                if(nSeparatorPosition > 0)
92                                {
93                                        Argument.m_sSwitchName = sArgument.Left(nSeparatorPosition);
94                                        Argument.m_sSwitchValue = sArgument.Mid(nSeparatorPosition + 1);
95                                        if(!Argument.m_sSwitchValue.IsEmpty())
96                                                Argument.m_bIntegerSwitchValueAvailable =  AtlStringToInteger(Argument.m_sSwitchValue, Argument.m_nIntegerSwitchValue);
97                                } else
98                                        Argument.m_sSwitchName = sArgument;
99                                return TRUE;
100                        }
101                        if(sArgument.GetLength() >= 2 && sArgument[0] == _T('"') && sArgument[sArgument.GetLength() - 1] == _T('"'))
102                                sArgument = sArgument.Mid(1, sArgument.GetLength() - 2);
103                        Argument.m_sValue = sArgument;
104                        return TRUE;
105                }
106        };
107
108        ////////////////////////////////////////////////////////
109        // CVideoMediaType
110
111        class CVideoMediaType
112        {
113        public:
114                DWORD m_nStreamIndex;
115                MF::CMediaType m_pMediaType;
116
117        public:
118        // CVideoMediaType
119                BOOL Initialize(const MF::CPresentationDescriptor& pPresentationDescriptor)
120                {
121                        DWORD nStreamCount;
122                        __C(pPresentationDescriptor->GetStreamDescriptorCount(&nStreamCount));
123                        for(DWORD nStreamIndex = 0; nStreamIndex < nStreamCount; nStreamIndex++)
124                        {
125                                BOOL bSelect;
126                                MF::CStreamDescriptor pStreamDescriptor;
127                                __C(pPresentationDescriptor->GetStreamDescriptorByIndex(nStreamIndex, &bSelect, &pStreamDescriptor));
128                                pStreamDescriptor.Trace();
129                                DWORD nStreamIdentifier;
130                                __C(pStreamDescriptor->GetStreamIdentifier(&nStreamIdentifier));
131                                CComPtr<IMFMediaTypeHandler> pMediaTypeHandler;
132                                __C(pStreamDescriptor->GetMediaTypeHandler(&pMediaTypeHandler));
133                                GUID MajorType;
134                                __C(pMediaTypeHandler->GetMajorType(&MajorType));
135                                _Z4(atlTraceGeneral, 4, _T("bSelect %d, nStreamIdentifier %d, MajorType %s\n"), bSelect, nStreamIdentifier, MF::FormatKey(MajorType));
136                                if(MajorType != MFMediaType_Video)
137                                        continue;
138                                DWORD nMediaTypeCount = 0;
139                                __C(pMediaTypeHandler->GetMediaTypeCount(&nMediaTypeCount));
140                                for(DWORD nMediaTypeIndex = 0; nMediaTypeIndex < nMediaTypeCount; nMediaTypeIndex++)
141                                {
142                                        MF::CMediaType pMediaType;
143                                        __C(pMediaTypeHandler->GetMediaTypeByIndex(nMediaTypeIndex, &pMediaType));
144                                        if(pMediaType.GetGUID(MF_MT_SUBTYPE) != MFVideoFormat_H264)
145                                                continue;
146                                        if(pMediaType.GetUINT32(MF_MT_VIDEO_PROFILE) != eAVEncH264VProfile_ConstrainedBase)
147                                                continue;
148                                        UINT32 nWidth, nHeight;
149                                        __C(MFGetAttributeSize(pMediaType, MF_MT_FRAME_SIZE, &nWidth, &nHeight));
150                                        if(nWidth != 1920 && nHeight != 1080)
151                                                continue;
152                                        UINT32 nRatioNumerator, nRatioDenominator;
153                                        __C(MFGetAttributeRatio(pMediaType, MF_MT_FRAME_RATE, &nRatioNumerator, &nRatioDenominator));
154                                        if(nRatioNumerator * 1 != nRatioDenominator * 30)
155                                                continue;
156                                        _Z4(atlTraceGeneral, 4, _T("nWidth %d, nHeight %d, nRatioNumerator %d, nRatioDenominator %d\n"), nWidth, nHeight, nRatioNumerator, nRatioDenominator);
157                                        pMediaType.Trace();
158                                        m_nStreamIndex = nStreamIndex;
159                                        m_pMediaType = pMediaType;
160                                        return TRUE;
161                                }
162                        }
163                        return FALSE;
164                }
165        };
166
167        ////////////////////////////////////////////////////////
168        // CAudioMediaType
169
170        class CAudioMediaType
171        {
172        public:
173                DWORD m_nStreamIndex;
174                MF::CMediaType m_pMediaType;
175
176        public:
177        // CAudioMediaType
178                BOOL Initialize(const MF::CPresentationDescriptor& pPresentationDescriptor)
179                {
180                        DWORD nStreamCount;
181                        __C(pPresentationDescriptor->GetStreamDescriptorCount(&nStreamCount));
182                        for(DWORD nStreamIndex = 0; nStreamIndex < nStreamCount; nStreamIndex++)
183                        {
184                                BOOL bSelect;
185                                MF::CStreamDescriptor pStreamDescriptor;
186                                __C(pPresentationDescriptor->GetStreamDescriptorByIndex(nStreamIndex, &bSelect, &pStreamDescriptor));
187                                pStreamDescriptor.Trace();
188                                DWORD nStreamIdentifier;
189                                __C(pStreamDescriptor->GetStreamIdentifier(&nStreamIdentifier));
190                                CComPtr<IMFMediaTypeHandler> pMediaTypeHandler;
191                                __C(pStreamDescriptor->GetMediaTypeHandler(&pMediaTypeHandler));
192                                GUID MajorType;
193                                __C(pMediaTypeHandler->GetMajorType(&MajorType));
194                                _Z4(atlTraceGeneral, 4, _T("bSelect %d, nStreamIdentifier %d, MajorType %s\n"), bSelect, nStreamIdentifier, MF::FormatKey(MajorType));
195                                if(MajorType != MFMediaType_Audio)
196                                        continue;
197                                DWORD nMediaTypeCount = 0;
198                                __C(pMediaTypeHandler->GetMediaTypeCount(&nMediaTypeCount));
199                                for(DWORD nMediaTypeIndex = 0; nMediaTypeIndex < nMediaTypeCount; nMediaTypeIndex++)
200                                {
201                                        MF::CMediaType pMediaType;
202                                        __C(pMediaTypeHandler->GetMediaTypeByIndex(nMediaTypeIndex, &pMediaType));
203                                        pMediaType.Trace();
204                                        m_nStreamIndex = nStreamIndex;
205                                        m_pMediaType = pMediaType;
206                                        return TRUE;
207                                }
208                        }
209                        return FALSE;
210                }
211        };
212
213        ////////////////////////////////////////////////////////
214        // CSourceReaderCallback
215
216        class ATL_NO_VTABLE CSourceReaderCallback :
217                public CComObjectRootEx<CComMultiThreadModelNoCS>,
218                public IMFSourceReaderCallback
219        {
220        public:
221
222        BEGIN_COM_MAP(CSourceReaderCallback)
223                COM_INTERFACE_ENTRY(IMFSourceReaderCallback)
224        END_COM_MAP()
225
226        public:
227
228                ////////////////////////////////////////////////////
229                // CStreamData
230
231                class CStreamData
232                {
233                public:
234                        LONGLONG m_nBaseTime;
235                        BOOL m_bBaseTimeAvailable;
236                        BOOL m_bCleanPointAvailable;
237                        CRoListT<MF::CSample> m_SampleList;
238                        LONGLONG m_nLastSampleTime;
239
240                public:
241                // CStreamData
242                        CStreamData() :
243                                m_bBaseTimeAvailable(FALSE),
244                                m_nLastSampleTime(0),
245                                m_bCleanPointAvailable(FALSE)
246                        {
247                        }
248                };
249
250        private:
251                mutable CRoCriticalSection m_DataCriticalSection;
252                CRoArrayT<CStreamData> m_StreamDataArray;
253                CEvent m_SampleAvailabilityEvent;
254                CComPtr<IMFSourceReader> m_pSourceReader;
255
256        public:
257        // CSourceReaderCallback
258                CSourceReaderCallback()
259                {
260                        _Z4_THIS();
261                }
262                ~CSourceReaderCallback()
263                {
264                        _Z4_THIS();
265                }
266                VOID Initialize(SIZE_T nStreamCount)
267                {
268                        _A(nStreamCount);
269                        CRoCriticalSectionLock DataLock(m_DataCriticalSection);
270                        _W(m_StreamDataArray.SetCount(nStreamCount));
271                        __E(m_SampleAvailabilityEvent.Create(NULL, TRUE, FALSE, NULL));
272                }
273                VOID Terminate()
274                {
275                        CRoCriticalSectionLock DataLock(m_DataCriticalSection);
276                        m_pSourceReader.Release();
277                }
278                VOID SetSourceReader(IMFSourceReader* pSourceReader)
279                {
280                        _A(pSourceReader);
281                        CRoCriticalSectionLock DataLock(m_DataCriticalSection);
282                        _A(!m_pSourceReader);
283                        m_pSourceReader = pSourceReader;
284                }
285                VOID ResetVideoCleanPointAvailable(DWORD nStreamIndex, LONGLONG nAdjustmentTime)
286                {
287                        _A(nAdjustmentTime >= 0 && nAdjustmentTime <= 1 * 1000 * 10000i64); // Reasonable adjustment
288                        CRoCriticalSectionLock DataLock(m_DataCriticalSection);
289                        _A(nStreamIndex < m_StreamDataArray.GetCount());
290                        CStreamData& StreamData = m_StreamDataArray[nStreamIndex];
291                        StreamData.m_bCleanPointAvailable = FALSE;
292                        for(DWORD nAnotherStreamIndex = 0; nAnotherStreamIndex < m_StreamDataArray.GetCount(); nStreamIndex++)
293                                if(nAnotherStreamIndex != nStreamIndex)
294                                {
295                                        CStreamData& StreamData = m_StreamDataArray[nAnotherStreamIndex];
296                                        _A(StreamData.m_bBaseTimeAvailable);
297                                        StreamData.m_nBaseTime += nAdjustmentTime;
298                                }
299                }
300                VOID ReadNextSample(DWORD nStreamIndex)
301                {
302                        CRoCriticalSectionLock DataLock(m_DataCriticalSection);
303                        __D(m_pSourceReader, E_NOINTERFACE);
304                        __C(m_pSourceReader->ReadSample(nStreamIndex, 0, NULL, NULL, NULL, NULL));
305                }
306                const CEvent& GetSampleAvailabilityEvent() const
307                {
308                        _A(m_SampleAvailabilityEvent);
309                        return m_SampleAvailabilityEvent;
310                }
311                VOID WriteSamples(IMFSinkWriter* pSinkWriter, CRoMapT<DWORD, DWORD>& StreamIndexMap)
312                {
313                        _A(pSinkWriter);
314                        for(DWORD nStreamIndex = 0; ; nStreamIndex++)
315                        {
316                                CRoCriticalSectionLock DataLock(m_DataCriticalSection);
317                                if(nStreamIndex >= m_StreamDataArray.GetCount())
318                                        break;
319                                DWORD nWriterStreamIndex;
320                                if(!StreamIndexMap.Lookup(nStreamIndex, nWriterStreamIndex))
321                                        continue;
322                                CStreamData& StreamData = m_StreamDataArray[nStreamIndex];
323                                for(; ; )
324                                {
325                                        if(StreamData.m_SampleList.IsEmpty())
326                                                break;
327                                        MF::CSample pSample = StreamData.m_SampleList.RemoveHead();
328                                        CRoCriticalSectionLock DataUnlock(m_DataCriticalSection);
329                                        __C(pSinkWriter->WriteSample(nWriterStreamIndex, pSample));
330                                }
331                        }
332                        CRoCriticalSectionLock DataLock(m_DataCriticalSection);
333                        for(auto&& StreamData: m_StreamDataArray)
334                                if(!StreamData.m_SampleList.IsEmpty())
335                                        return;
336                        _W(m_SampleAvailabilityEvent.Reset());
337                }
338                SIZE_T GetLastListSampleTime(DWORD nStreamIndex, LONGLONG& nTime) const
339                {
340                        CRoCriticalSectionLock DataLock(m_DataCriticalSection);
341                        _A(nStreamIndex < m_StreamDataArray.GetCount());
342                        const CStreamData& StreamData = m_StreamDataArray[nStreamIndex];
343                        if(StreamData.m_SampleList.IsEmpty())
344                                return 0;
345                        __C(StreamData.m_SampleList.GetTail()->GetSampleTime(&nTime));
346                        return StreamData.m_SampleList.GetCount();
347                }
348                LONGLONG GetLastSampleTime() const
349                {
350                        LONGLONG nLastSampleTime = 0;
351                        CRoCriticalSectionLock DataLock(m_DataCriticalSection);
352                        for(auto&& StreamData: m_StreamDataArray)
353                                if(nLastSampleTime < StreamData.m_nLastSampleTime)
354                                        nLastSampleTime = StreamData.m_nLastSampleTime;
355                        return nLastSampleTime;
356                }
357
358        // IMFSourceReaderCallback
359        STDMETHOD(OnReadSample)(HRESULT nStatus, DWORD nStreamIndex, DWORD nStreamFlags, LONGLONG nTime, IMFSample* pSample) override
360                {
361                        _Z4(atlTraceCOM, 4, _T("nStatus 0x%08X, nStreamIndex %d, nStreamFlags %s, nTime %s, pSample 0x%p\n"), nStatus, nStreamIndex, MF::FormatSourceReaderStreamFlags(nStreamFlags), MF::FormatNanoTime(nTime), pSample);
362                        _ATLTRY
363                        {
364                                {
365                                        if(FAILED(nStatus))
366                                        {
367                                                _Z4(atlTraceGeneral, 4, _T("nStatus %s\n"), MF::FormatResult(nStatus));
368                                                _A(nStreamFlags & MF_SOURCE_READERF_ERROR);
369                                                _tprintf(_T("OnReadSample Error: %s, stream %d\n"), MF::FormatResult(nStatus), nStreamIndex);
370                                                return S_OK;
371                                        }
372                                        CRoCriticalSectionLock DataLock(m_DataCriticalSection);
373                                        __D(nStreamIndex < m_StreamDataArray.GetCount(), E_INVALIDARG);
374                                        CStreamData& StreamData = m_StreamDataArray[nStreamIndex];
375                                        if(nStreamFlags & MF_SOURCE_READERF_STREAMTICK)
376                                        {
377                                                _A(!pSample);
378                                                _A(!StreamData.m_bBaseTimeAvailable);
379                                                StreamData.m_nBaseTime = nTime;
380                                                StreamData.m_bBaseTimeAvailable = TRUE;
381                                        } else
382                                        {
383                                                MF::CSample& pSampleEx = reinterpret_cast<MF::CSample&>(pSample);
384                                        //      if(nStreamIndex == 1)
385                                        //              pSampleEx.Trace();
386                                                // SUGG: Here is the right place to review samples and implement skipping in case certain data is lost in the middle of capture
387                                                //       of compressed data
388                                //              BOOL bSkip = FALSE;
389                                //              if(!StreamData.m_bCleanPointAvailable)
390                                //              {
391                                //                      UINT32 nCleanPoint;
392                                //                      if(pSampleEx.TryGetUINT32(MFSampleExtension_CleanPoint, nCleanPoint))
393                                //                              bSkip = !nCleanPoint;
394                                //              }
395                                //              if(!bSkip)
396                                                {
397                                                        StreamData.m_bCleanPointAvailable = TRUE;
398                                                        _A(StreamData.m_bBaseTimeAvailable);
399                                                        LONGLONG nSampleTime;
400                                                        __C(pSampleEx->GetSampleTime(&nSampleTime));
401                                                        nSampleTime -= StreamData.m_nBaseTime;
402                                                        __C(pSampleEx->SetSampleTime(nSampleTime));
403                                                        _W(StreamData.m_SampleList.AddTail(pSampleEx));
404                                                        StreamData.m_nLastSampleTime = nSampleTime;
405                                                }
406                                        }
407                                }
408                                ReadNextSample(nStreamIndex);
409                                _W(m_SampleAvailabilityEvent.Set());
410                        }
411                        _ATLCATCHALL()
412                        {
413                                _Z_EXCEPTION();
414                        }
415                        return S_OK;
416                }
417        STDMETHOD(OnFlush)(DWORD nStreamIndex) override
418                {
419                        _Z4(atlTraceCOM, 4, _T("nStreamIndex %d\n"), nStreamIndex);
420                        return S_OK;
421                }
422        STDMETHOD(OnEvent)(DWORD nStreamIndex, IMFMediaEvent* pEvent) override
423                {
424                        _Z4(atlTraceCOM, 4, _T("nStreamIndex %d, pEvent 0x%p\n"), nStreamIndex, pEvent);
425                        _ATLTRY
426                        {
427                                reinterpret_cast<const MF::CMediaEvent&>(pEvent).Trace();
428                        }
429                        _ATLCATCHALL()
430                        {
431                                _Z_EXCEPTION();
432                        }
433                        return S_OK;
434                }
435        };
436
437        ////////////////////////////////////////////////////////
438        // CFileByteStream
439
440        class ATL_NO_VTABLE CFileByteStream :
441                public CComObjectRootEx<CComMultiThreadModelNoCS>,
442                public IMFByteStream,
443                public IMFSampleOutputStream
444        {
445        public:
446
447        DECLARE_QI_TRACE(CFileByteStream)
448
449        BEGIN_COM_MAP(CFileByteStream)
450                COM_INTERFACE_ENTRY(IMFByteStream)
451                //COM_INTERFACE_ENTRY(IMFSampleOutputStream)
452        END_COM_MAP()
453
454        public:
455
456                ////////////////////////////////////////////////////////
457                // CWriteObject
458
459                class ATL_NO_VTABLE __declspec(uuid("{C0368834-0679-4720-A8C4-E0A68E4F2750}")) CWriteObject :
460                        public CComObjectRootEx<CComMultiThreadModelNoCS>,
461                        public IUnknown
462                {
463                public:
464
465                //DECLARE_QI_TRACE(CWriteObject)
466
467                BEGIN_COM_MAP(CWriteObject)
468                        COM_INTERFACE_ENTRY_IID(__uuidof(CWriteObject), CWriteObject)
469                END_COM_MAP()
470
471                public:
472                        ULONG m_nDataSize;
473
474                public:
475                // CWriteObject
476                        CWriteObject() :
477                                m_nDataSize(0)
478                        {
479                                _Z5_THIS();
480                        }
481                        ~CWriteObject()
482                        {
483                                _Z5_THIS();
484                        }
485                };
486
487        private:
488                mutable CRoCriticalSection m_DataCriticalSection;
489                CAtlFile m_File;
490                CHeapPtr<BYTE> m_pnData;
491                ULONGLONG m_nDataCapacity;
492                ULONGLONG m_nDataSize;
493                ULONGLONG m_nDataPosition;
494
495                VOID SetDataCapacity(ULONGLONG nDataCapacity)
496                {
497                        if(nDataCapacity <= m_nDataCapacity)
498                                return;
499                        static const SIZE_T g_nDataGranularity = 1 << 20; // 1 MB
500                        _A(!(g_nDataGranularity & (g_nDataGranularity - 1)));
501                        nDataCapacity += (g_nDataGranularity - 1);
502                        nDataCapacity &= (ULONGLONG) ~(g_nDataGranularity - 1);
503                        __D(m_pnData.Reallocate((SIZE_T) nDataCapacity), E_OUTOFMEMORY);
504                        m_nDataCapacity = nDataCapacity;
505                }
506
507        public:
508        // CFileByteStream
509                CFileByteStream() :
510                        m_nDataCapacity(0),
511                        m_nDataSize(0),
512                        m_nDataPosition(0)
513                {
514                        _Z4_THIS();
515                }
516                ~CFileByteStream()
517                {
518                        _Z4_THIS();
519                }
520                VOID Initialize(LPCTSTR pszPath)
521                {
522                        //CRoCriticalSectionLock DataLock(m_DataCriticalSection);
523                        __C(m_File.Create(pszPath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, CREATE_ALWAYS));
524                }
525
526        // IMFByteStream
527                STDMETHOD(GetCapabilities)(DWORD* pnCapabilities) override
528                {
529                        _Z4(atlTraceCOM, 4, _T("this 0x%p\n"), this);
530                        _ATLTRY
531                        {
532                                __D(pnCapabilities, E_POINTER);
533                                //CRoCriticalSectionLock DataLock(m_DataCriticalSection);
534                                *pnCapabilities = MFBYTESTREAM_IS_READABLE | MFBYTESTREAM_IS_WRITABLE | MFBYTESTREAM_IS_SEEKABLE | MFBYTESTREAM_DOES_NOT_USE_NETWORK;
535                        }
536                        _ATLCATCH(Exception)
537                        {
538                                _C(Exception);
539                        }
540                        return S_OK;
541                }
542                STDMETHOD(GetLength)(QWORD* pnLength) override
543                {
544                        _Z4(atlTraceCOM, 4, _T("this 0x%p\n"), this);
545                        _ATLTRY
546                        {
547                                __D(pnLength, E_POINTER);
548                                CRoCriticalSectionLock DataLock(m_DataCriticalSection);
549                                *pnLength = m_nDataSize;
550                        }
551                        _ATLCATCH(Exception)
552                        {
553                                _C(Exception);
554                        }
555                        return S_OK;
556                }
557                STDMETHOD(SetLength)(QWORD nLength) override
558                {
559                        _Z4(atlTraceCOM, 4, _T("this 0x%p, nLength %I64d\n"), this, nLength);
560                        _ATLTRY
561                        {
562                                CRoCriticalSectionLock DataLock(m_DataCriticalSection);
563                                SetDataCapacity(nLength);
564                                m_nDataSize = nLength;
565                                if(m_nDataPosition > m_nDataSize)
566                                        m_nDataPosition = m_nDataSize;
567                        }
568                        _ATLCATCH(Exception)
569                        {
570                                _C(Exception);
571                        }
572                        return S_OK;
573                }
574                STDMETHOD(GetCurrentPosition)(QWORD* pnPosition) override
575                {
576                        _Z4(atlTraceCOM, 4, _T("this 0x%p\n"), this);
577                        _ATLTRY
578                        {
579                                __D(pnPosition, E_POINTER);
580                                CRoCriticalSectionLock DataLock(m_DataCriticalSection);
581                                *pnPosition = m_nDataPosition;
582                        }
583                        _ATLCATCH(Exception)
584                        {
585                                _C(Exception);
586                        }
587                        return S_OK;
588                }
589                STDMETHOD(SetCurrentPosition)(QWORD nPosition) override
590                {
591                        _Z4(atlTraceCOM, 4, _T("this 0x%p, nPosition %I64d\n"), this, nPosition);
592                        _ATLTRY
593                        {
594                                CRoCriticalSectionLock DataLock(m_DataCriticalSection);
595                                __D(nPosition <= m_nDataSize, E_INVALIDARG);
596                                m_nDataPosition = nPosition;
597                        }
598                        _ATLCATCH(Exception)
599                        {
600                                _C(Exception);
601                        }
602                        return S_OK;
603                }
604                STDMETHOD(IsEndOfStream)(BOOL* pbEndOfStream) override
605                {
606                        _Z4(atlTraceCOM, 4, _T("this 0x%p\n"), this);
607                        _ATLTRY
608                        {
609                                __D(pbEndOfStream, E_POINTER);
610                                CRoCriticalSectionLock DataLock(m_DataCriticalSection);
611                                *pbEndOfStream = m_nDataPosition >= m_nDataSize;
612                        }
613                        _ATLCATCH(Exception)
614                        {
615                                _C(Exception);
616                        }
617                        return S_OK;
618                }
619                STDMETHOD(Read)(BYTE* pnData, ULONG nDataCapacity, ULONG* pnDataSize) override
620                {
621                        _Z4(atlTraceCOM, 4, _T("this 0x%p, nDataCapacity %d\n"), this, nDataCapacity);
622                        _ATLTRY
623                        {
624                                // TODO: ...
625                                _C(E_NOTIMPL);
626                        }
627                        _ATLCATCH(Exception)
628                        {
629                                _C(Exception);
630                        }
631                        return S_OK;
632                }
633                STDMETHOD(BeginRead)(BYTE* pnData, ULONG nDataCapacity, IMFAsyncCallback* pCallback, IUnknown* pState) override
634                {
635                        _Z4(atlTraceCOM, 4, _T("this 0x%p, nDataCapacity %d\n"), this, nDataCapacity);
636                        _ATLTRY
637                        {
638                                // TODO: ...
639                                _C(E_NOTIMPL);
640                        }
641                        _ATLCATCH(Exception)
642                        {
643                                _C(Exception);
644                        }
645                        return S_OK;
646                }
647                STDMETHOD(EndRead)(IMFAsyncResult* pResult, ULONG* pnDataSize) override
648                {
649                        _Z4(atlTraceCOM, 4, _T("this 0x%p\n"), this);
650                        _ATLTRY
651                        {
652                                // TODO: ...
653                                _C(E_NOTIMPL);
654                        }
655                        _ATLCATCH(Exception)
656                        {
657                                _C(Exception);
658                        }
659                        return S_OK;
660                }
661                STDMETHOD(Write)(const BYTE* pnData, ULONG nDataSize, ULONG* pnWriteDataSize) override
662                {
663                        _Z4(atlTraceCOM, 4, _T("this 0x%p, nDataSize %d\n"), this, nDataSize);
664                        _ATLTRY
665                        {
666                                __D(pnData || !nDataSize, E_INVALIDARG);
667                                __D(pnWriteDataSize, E_POINTER);
668                                CRoCriticalSectionLock DataLock(m_DataCriticalSection);
669                                if(nDataSize)
670                                {
671                                        SetDataCapacity(m_nDataPosition + nDataSize);
672                                        memcpy(m_pnData + (SIZE_T) m_nDataPosition, pnData, nDataSize);
673                                        m_nDataPosition += nDataSize;
674                                        if(m_nDataPosition > m_nDataSize)
675                                                m_nDataSize = m_nDataPosition;
676                                }
677                                *pnWriteDataSize = nDataSize;
678                        }
679                        _ATLCATCH(Exception)
680                        {
681                                _C(Exception);
682                        }
683                        return S_OK;
684                }
685                STDMETHOD(BeginWrite)(const BYTE* pnData, ULONG nDataSize, IMFAsyncCallback* pCallback, IUnknown* pState) override
686                {
687                        _Z5(atlTraceCOM, 5, _T("this 0x%p, nDataSize %d, pCallback 0x%p, pState 0x%p\n"), this, nDataSize, pCallback, pState);
688                        _ATLTRY
689                        {
690                                CLocalObjectPtr<CWriteObject> pWriteObject;
691                                __C(Write(pnData, nDataSize, &pWriteObject->m_nDataSize));
692                                CComPtr<IMFAsyncResult> pAsyncResult;
693                                __C(MFCreateAsyncResult(pWriteObject, pCallback, pState, &pAsyncResult));
694                                __C(pCallback->Invoke(pAsyncResult));
695                        }
696                        _ATLCATCH(Exception)
697                        {
698                                _C(Exception);
699                        }
700                        return S_OK;
701                }
702                STDMETHOD(EndWrite)(IMFAsyncResult* pResult, ULONG* pnDataSize) override
703                {
704                        _Z5(atlTraceCOM, 5, _T("this 0x%p, pResult 0x%p\n"), this, pResult);
705                        _ATLTRY
706                        {
707                                __D(pResult, E_INVALIDARG);
708                                __D(pnDataSize, E_POINTER);
709                                CComPtr<IUnknown> pObjectUnknown;
710                                __C(pResult->GetObject(&pObjectUnknown));
711                                CWriteObject* pWriteObject = static_cast<CWriteObject*>((IUnknown*) pObjectUnknown);
712                                _A(pWriteObject == (CWriteObject*) CComQIPtr<CWriteObject>(pObjectUnknown));
713                                *pnDataSize = pWriteObject->m_nDataSize;
714                        }
715                        _ATLCATCH(Exception)
716                        {
717                                _C(Exception);
718                        }
719                        return S_OK;
720                }
721                STDMETHOD(Seek)(MFBYTESTREAM_SEEK_ORIGIN Origin, LONGLONG nOffset, DWORD nFlags, QWORD* pnPosition) override
722                {
723                        _Z4(atlTraceCOM, 4, _T("this 0x%p, Origin %d, nOffset %I64d, nFlags 0x%X\n"), this, Origin, nOffset, nFlags);
724                        _ATLTRY
725                        {
726                                // TODO: ...
727                                _C(E_NOTIMPL);
728                        }
729                        _ATLCATCH(Exception)
730                        {
731                                _C(Exception);
732                        }
733                        return S_OK;
734                }
735                STDMETHOD(Flush)() override
736                {
737                        _Z4(atlTraceCOM, 4, _T("this 0x%p\n"), this);
738                        _ATLTRY
739                        {
740                                // TODO: ...
741                        }
742                        _ATLCATCH(Exception)
743                        {
744                                _C(Exception);
745                        }
746                        return S_OK;
747                }
748                STDMETHOD(Close)() override
749                {
750                        _Z4(atlTraceCOM, 4, _T("this 0x%p\n"), this);
751                        _ATLTRY
752                        {
753                                CRoCriticalSectionLock DataLock(m_DataCriticalSection);
754                                __D(m_File, E_UNNAMED);
755                                __C(m_File.Write(m_pnData, (DWORD) m_nDataSize));
756                                m_File.Close();
757                        }
758                        _ATLCATCH(Exception)
759                        {
760                                _C(Exception);
761                        }
762                        return S_OK;
763                }
764
765        // IMFSampleOutputStream
766                STDMETHOD(BeginWriteSample)(IMFSample* pSample, IMFAsyncCallback* pCallback, IUnknown* pState) override
767                {
768                        _Z4(atlTraceCOM, 4, _T("this 0x%p, pSample 0x%p, pCallback 0x%p, pState 0x%p\n"), this, pSample, pCallback, pState);
769                        _ATLTRY
770                        {
771                                // TODO: ...
772                                _C(E_NOTIMPL);
773                        }
774                        _ATLCATCH(Exception)
775                        {
776                                _C(Exception);
777                        }
778                        return S_OK;
779                }
780                STDMETHOD(EndWriteSample)(IMFAsyncResult* pResult) override
781                {   
782                        _Z4(atlTraceCOM, 4, _T("this 0x%p, pResult 0x%p\n"), this, pResult);
783                        _ATLTRY
784                        {
785                                // TODO: ...
786                                _C(E_NOTIMPL);
787                        }
788                        _ATLCATCH(Exception)
789                        {
790                                _C(Exception);
791                        }
792                        return S_OK;
793                }
794                //STDMETHOD(Close)() override
795        };
796
797        ////////////////////////////////////////////////////////
798        // CSessionT
799
800        template <typename CVideoMediaType, typename CAudioMediaType>
801        class CSessionT
802        {
803        public:
804                CVideoMediaType m_VideoMediaType;
805                CAudioMediaType m_AudioMediaType;
806                CComPtr<IMFMediaSource> m_pMediaSource;
807                CComPtr<IMFSourceReader> m_pSourceReader;
808                CObjectPtr<CSourceReaderCallback> m_pSourceReaderCallback;
809                CPath m_sPath;
810                CComPtr<IMFSinkWriter> m_pSinkWriter;
811                CRoMapT<DWORD, DWORD> m_StreamIndexMap;
812
813        public:
814        // CSessionT
815                VOID Initialize(const CString& sVideoIdentifier, UINT32 nBitrate, const CString& sAudioIdentifier, LPCTSTR pszPath = NULL)
816                {
817                        #pragma region Media Source
818                        CComPtr<IMFMediaSource> pVideoMediaSource, pAudioMediaSource;
819                        CComPtr<IMFMediaSource> pMediaSource;
820                        {
821                                {
822                                        MF::CAttributes pAttributes;
823                                        pAttributes.Create(2);
824                                        pAttributes[MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE] = MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID;
825                                        pAttributes[MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK] = sVideoIdentifier;
826                                        pVideoMediaSource = MF::CActivate::CreateDeviceSource(pAttributes);
827                                }
828                                {
829                                        MF::CAttributes pAttributes;
830                                        pAttributes.Create(2);
831                                        pAttributes[MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE] = MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_AUDCAP_GUID;
832                                        pAttributes[MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_AUDCAP_ENDPOINT_ID] = sAudioIdentifier;
833                                        pAudioMediaSource = MF::CActivate::CreateDeviceSource(pAttributes);
834                                }
835                                _A(pVideoMediaSource && pAudioMediaSource);
836                                CComPtr<IMFCollection> pCollection;
837                                __C(MFCreateCollection(&pCollection));
838                                __C(pCollection->AddElement(pVideoMediaSource));
839                                __C(pCollection->AddElement(pAudioMediaSource));
840                                __C(MFCreateAggregateSource(pCollection, &pMediaSource));
841                        }
842                        m_pMediaSource = pMediaSource;
843                        #pragma endregion
844                        DWORD nCharacteristics;
845                        __C(pMediaSource->GetCharacteristics(&nCharacteristics));
846                        _Z4(atlTraceGeneral, 4, _T("nCharacteristics %s\n"), MF::FormatMediaSourceCharacteristics(nCharacteristics));
847                        MF::CPresentationDescriptor pPresentationDescriptor;
848                        __C(pMediaSource->CreatePresentationDescriptor(&pPresentationDescriptor));
849                        pPresentationDescriptor.Trace();
850                        __D(m_VideoMediaType.Initialize(pPresentationDescriptor), E_UNNAMED);
851                        __D(m_AudioMediaType.Initialize(pPresentationDescriptor), E_UNNAMED);
852                        if(nBitrate)
853                                m_VideoMediaType.m_pMediaType[MF_MT_AVG_BITRATE] = nBitrate;
854                        {
855                                DWORD nStreamCount = 0;
856                                __C(pPresentationDescriptor->GetStreamDescriptorCount(&nStreamCount));
857                                _A(m_VideoMediaType.m_nStreamIndex != m_AudioMediaType.m_nStreamIndex);
858                                _A(m_VideoMediaType.m_nStreamIndex < nStreamCount && m_AudioMediaType.m_nStreamIndex < nStreamCount);
859                                for(DWORD nAnotherStreamIndex = 0; nAnotherStreamIndex < nStreamCount; nAnotherStreamIndex++)
860                                        if(nAnotherStreamIndex != m_VideoMediaType.m_nStreamIndex && nAnotherStreamIndex != m_AudioMediaType.m_nStreamIndex)
861                                                __C(pPresentationDescriptor->DeselectStream(nAnotherStreamIndex));
862                                __C(pPresentationDescriptor->SelectStream(m_VideoMediaType.m_nStreamIndex));
863                                __C(pPresentationDescriptor->SelectStream(m_AudioMediaType.m_nStreamIndex));
864                                m_pSourceReaderCallback.Construct();
865                                m_pSourceReaderCallback->Initialize(nStreamCount);
866                        }
867                        MF::CAttributes pAttributes;
868                        pAttributes.Create(2);
869                        //__C(pAttributes->SetUINT32(MF_READWRITE_DISABLE_CONVERTERS, TRUE));
870                        // NOTE: Using the Source Reader in Asynchronous Mode https://msdn.microsoft.com/en-us/library/windows/desktop/gg583871
871                        __C(pAttributes->SetUnknown(MF_SOURCE_READER_ASYNC_CALLBACK, m_pSourceReaderCallback));
872                        CComPtr<IMFSourceReader> pSourceReader;
873                        __C(MFCreateSourceReaderFromMediaSource(pMediaSource, pAttributes, &pSourceReader));
874                        m_pSourceReaderCallback->SetSourceReader(pSourceReader);
875                        {
876                                DWORD nStreamCount = 0;
877                                __C(pPresentationDescriptor->GetStreamDescriptorCount(&nStreamCount));
878                                for(DWORD nAnotherStreamIndex = 0; nAnotherStreamIndex < nStreamCount; nAnotherStreamIndex++)
879                                        if(nAnotherStreamIndex != m_VideoMediaType.m_nStreamIndex && nAnotherStreamIndex != m_AudioMediaType.m_nStreamIndex)
880                                                __C(pSourceReader->SetStreamSelection(nAnotherStreamIndex, FALSE));
881                                __C(pSourceReader->SetStreamSelection(m_VideoMediaType.m_nStreamIndex, TRUE));
882                                __C(pSourceReader->SetStreamSelection(m_AudioMediaType.m_nStreamIndex, TRUE));
883                                __C(pSourceReader->SetCurrentMediaType(m_VideoMediaType.m_nStreamIndex, NULL, m_VideoMediaType.m_pMediaType));
884                                __C(pSourceReader->SetCurrentMediaType(m_AudioMediaType.m_nStreamIndex, NULL, m_AudioMediaType.m_pMediaType));
885                        }
886                        //__C(pMediaSource->Start(pPresentationDescriptor, NULL, NULL));
887                        MF::CCodecApi pCodecApi(pVideoMediaSource, TRUE);
888                        if(pCodecApi)
889                        {
890                                if(nBitrate)
891                                {
892                                        pCodecApi[CODECAPI_AVEncCommonRateControlMode] = (UINT32) eAVEncCommonRateControlMode_CBR;
893                                        pCodecApi[CODECAPI_AVEncCommonMeanBitRate] = (UINT32) nBitrate;
894                                }
895                                //pCodecApi[CODECAPI_AVEncMPVGOPSize] = (UINT32) 30;
896                        }
897                        m_sPath = pszPath; 
898                        if(!_tcslen(m_sPath))
899                                m_sPath = CreateOutputPath();
900                        // NOTE: MFCreateSinkWriterFromURL https://msdn.microsoft.com/en-us/library/windows/desktop/dd388105
901                        CComPtr<IMFByteStream> pByteStream;
902                        MF::CAttributes pSinkAttributes;
903                        #if FALSE
904                                CObjectPtr<CFileByteStream> pFileByteStream;
905                                pFileByteStream.Construct()->Initialize(m_sPath);
906                                pByteStream = pFileByteStream;
907                                // WARN: It looks like this cannot have effect when used like this; the attribute is apparently NOT working the supposed way that
908                                //       media sink somehow sees it and update the media file. I suppose instead it is rather Transcode API attribute (despite the
909                                //       naming) and is handled through API patch that internally applies additional re-muxing behind the scenes...
910                                pSinkAttributes.Create(1);
911                                pSinkAttributes[MF_MPEG4SINK_MOOV_BEFORE_MDAT] = (UINT32) 1;
912                                #if TRUE
913                                        CObjectPtr<MF::Private::CAttributes> pPrivateAttributes;
914                                        pPrivateAttributes.Construct()->Initialize(pSinkAttributes);
915                                        pSinkAttributes.m_p = (IMFAttributes*) pPrivateAttributes;
916                                #endif
917                                // NOTE: This is a development code path, see if(m_pSinkWriter) workaround below
918                                //__C(MFCreateSinkWriterFromURL(CT2CW(m_sPath), pByteStream, pSinkAttributes, &m_pSinkWriter));
919                        #else
920                                __C(MFCreateSinkWriterFromURL(CT2CW(m_sPath), pByteStream, pSinkAttributes, &m_pSinkWriter));
921                        #endif
922                        DWORD nVideoWriterStreamIndex, nAudioWriterStreamIndex;
923                        #pragma region H.264 Video
924                        MF::CMediaType pVideoMediaType = m_VideoMediaType.m_pMediaType;
925                        if(pVideoMediaType.GetGUID(MF_MT_SUBTYPE) != MFVideoFormat_H264)
926                        {
927                                pVideoMediaType.Trace();
928                                pVideoMediaType.Release();
929                                pVideoMediaType.Create();
930                                // NOTE: H.264 Video Encoder https://msdn.microsoft.com/en-us/library/windows/desktop/dd797816
931                                pVideoMediaType[MF_MT_MAJOR_TYPE] = MFMediaType_Video;
932                                pVideoMediaType[MF_MT_SUBTYPE] = MFVideoFormat_H264;
933                                pVideoMediaType[MF_MT_AVG_BITRATE] = (UINT32) 2048000; // 2 MBps
934                                pVideoMediaType[MF_MT_FRAME_RATE] = m_VideoMediaType.m_pMediaType.GetUINT64(MF_MT_FRAME_RATE);
935                                pVideoMediaType[MF_MT_FRAME_SIZE] = m_VideoMediaType.m_pMediaType.GetUINT64(MF_MT_FRAME_SIZE);
936                                pVideoMediaType[MF_MT_INTERLACE_MODE] = m_VideoMediaType.m_pMediaType.GetUINT32(MF_MT_INTERLACE_MODE);
937                                pVideoMediaType[MF_MT_PIXEL_ASPECT_RATIO] = m_VideoMediaType.m_pMediaType.GetUINT64(MF_MT_PIXEL_ASPECT_RATIO);
938                        }
939                        if(m_pSinkWriter)
940                        {
941                                __C(m_pSinkWriter->AddStream(pVideoMediaType, &nVideoWriterStreamIndex));
942                                __C(m_pSinkWriter->SetInputMediaType(nVideoWriterStreamIndex, m_VideoMediaType.m_pMediaType, NULL));
943                        }
944                        #pragma endregion
945                        #pragma region AAC Audio
946                        _A(m_AudioMediaType.m_pMediaType.GetUINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND) == 48000);
947                        _A(m_AudioMediaType.m_pMediaType.GetUINT32(MF_MT_AUDIO_NUM_CHANNELS) == 2);
948                        MF::CMediaType pAudioMediaType;
949                        pAudioMediaType.Create();
950                        // NOTE: AAC Encoder https://msdn.microsoft.com/en-us/library/windows/desktop/dd742785
951                        pAudioMediaType[MF_MT_MAJOR_TYPE] = MFMediaType_Audio;
952                        pAudioMediaType[MF_MT_SUBTYPE] = MFAudioFormat_AAC;
953                        pAudioMediaType[MF_MT_AUDIO_BITS_PER_SAMPLE] = (UINT32) 16;
954                        pAudioMediaType[MF_MT_AUDIO_SAMPLES_PER_SECOND] = (UINT32) m_AudioMediaType.m_pMediaType.GetUINT32(MF_MT_AUDIO_SAMPLES_PER_SECOND);
955                        pAudioMediaType[MF_MT_AUDIO_NUM_CHANNELS] = (UINT32) m_AudioMediaType.m_pMediaType.GetUINT32(MF_MT_AUDIO_NUM_CHANNELS);
956                        pAudioMediaType[MF_MT_AUDIO_AVG_BYTES_PER_SECOND] = (UINT32) 20000;
957                        if(m_pSinkWriter)
958                        {
959                                __C(m_pSinkWriter->AddStream(pAudioMediaType, &nAudioWriterStreamIndex));
960                                __C(m_pSinkWriter->SetInputMediaType(nAudioWriterStreamIndex, m_AudioMediaType.m_pMediaType, NULL));
961                        }
962                        #pragma endregion
963                        // NOTE: See IMFSinkWriterEx::GetTransformForStream https://msdn.microsoft.com/en-us/library/windows/desktop/hh448061
964                        //       which might possibly indicate internal MFTs such as, for example, format conversion and audio encoder for AAC audio
965                        _A(m_StreamIndexMap.IsEmpty());
966                        if(m_pSinkWriter)
967                        {
968                                m_StreamIndexMap[m_VideoMediaType.m_nStreamIndex] = nVideoWriterStreamIndex;
969                                m_StreamIndexMap[m_AudioMediaType.m_nStreamIndex] = nAudioWriterStreamIndex;
970                        } else
971                        {
972                                // TODO: Add audio support
973                                pAudioMediaType.Release();
974                                CComPtr<IMFMediaSink> pMediaSink;
975                                __C(MFCreateMPEG4MediaSink(pByteStream, pVideoMediaType, pAudioMediaType, &pMediaSink));
976                                __C(MFCreateSinkWriterFromMediaSink(pMediaSink, pSinkAttributes, &m_pSinkWriter));
977                                nVideoWriterStreamIndex = 0;
978                        //      __C(m_pSinkWriter->AddStream(pVideoMediaType, &nVideoWriterStreamIndex)); // MF_E_STREAMSINKS_FIXED
979                                __C(m_pSinkWriter->SetInputMediaType(nVideoWriterStreamIndex, m_VideoMediaType.m_pMediaType, NULL));
980                        //      //__C(m_pSinkWriter->AddStream(pAudioMediaType, &nAudioWriterStreamIndex));
981                        //      //__C(m_pSinkWriter->SetInputMediaType(nAudioWriterStreamIndex, m_AudioMediaType.m_pMediaType, NULL));
982                                m_StreamIndexMap[m_VideoMediaType.m_nStreamIndex] = nVideoWriterStreamIndex;
983                        }
984                }
985                HANDLE GetSampleAvailabilityEvent() const
986                {
987                        return m_pSourceReaderCallback ? (HANDLE) m_pSourceReaderCallback->GetSampleAvailabilityEvent() : NULL;
988                }
989        };
990
991        ////////////////////////////////////////////////////////
992        // CSecondaryVideoMediaType
993
994        class CSecondaryVideoMediaType
995        {
996        public:
997                DWORD m_nStreamIndex;
998                MF::CMediaType m_pMediaType;
999
1000        public:
1001        // CSecondaryVideoMediaType
1002                BOOL Initialize(const MF::CPresentationDescriptor& pPresentationDescriptor)
1003                {
1004                        DWORD nStreamCount;
1005                        __C(pPresentationDescriptor->GetStreamDescriptorCount(&nStreamCount));
1006                        for(DWORD nStreamIndex = 0; nStreamIndex < nStreamCount; nStreamIndex++)
1007                        {
1008                                BOOL bSelect;
1009                                MF::CStreamDescriptor pStreamDescriptor;
1010                                __C(pPresentationDescriptor->GetStreamDescriptorByIndex(nStreamIndex, &bSelect, &pStreamDescriptor));
1011                                pStreamDescriptor.Trace();
1012                                DWORD nStreamIdentifier;
1013                                __C(pStreamDescriptor->GetStreamIdentifier(&nStreamIdentifier));
1014                                CComPtr<IMFMediaTypeHandler> pMediaTypeHandler;
1015                                __C(pStreamDescriptor->GetMediaTypeHandler(&pMediaTypeHandler));
1016                                GUID MajorType;
1017                                __C(pMediaTypeHandler->GetMajorType(&MajorType));
1018                                _Z4(atlTraceGeneral, 4, _T("bSelect %d, nStreamIdentifier %d, MajorType %s\n"), bSelect, nStreamIdentifier, MF::FormatKey(MajorType));
1019                                if(MajorType != MFMediaType_Video)
1020                                        continue;
1021                                DWORD nMediaTypeCount = 0;
1022                                __C(pMediaTypeHandler->GetMediaTypeCount(&nMediaTypeCount));
1023                                for(DWORD nMediaTypeIndex = 0; nMediaTypeIndex < nMediaTypeCount; nMediaTypeIndex++)
1024                                {
1025                                        MF::CMediaType pMediaType;
1026                                        __C(pMediaTypeHandler->GetMediaTypeByIndex(nMediaTypeIndex, &pMediaType));
1027                                        if(pMediaType.GetGUID(MF_MT_SUBTYPE) != MFVideoFormat_YUY2)
1028                                                continue;
1029                                        UINT32 nWidth, nHeight;
1030                                        __C(MFGetAttributeSize(pMediaType, MF_MT_FRAME_SIZE, &nWidth, &nHeight));
1031                                        if(nWidth != 640 && nHeight != 480)
1032                                                continue;
1033                                        UINT32 nRatioNumerator, nRatioDenominator;
1034                                        __C(MFGetAttributeRatio(pMediaType, MF_MT_FRAME_RATE, &nRatioNumerator, &nRatioDenominator));
1035                                        if(nRatioNumerator * 1 != nRatioDenominator * 30)
1036                                                continue;
1037                                        _Z4(atlTraceGeneral, 4, _T("nWidth %d, nHeight %d, nRatioNumerator %d, nRatioDenominator %d\n"), nWidth, nHeight, nRatioNumerator, nRatioDenominator);
1038                                        pMediaType.Trace();
1039                                        m_nStreamIndex = nStreamIndex;
1040                                        m_pMediaType = pMediaType;
1041                                        return TRUE;
1042                                }
1043                        }
1044                        return FALSE;
1045                }
1046        };
1047
1048        ////////////////////////////////////////////////////////
1049        // CSecondaryAudioMediaType
1050
1051        class CSecondaryAudioMediaType
1052        {
1053        public:
1054                DWORD m_nStreamIndex;
1055                MF::CMediaType m_pMediaType;
1056
1057        public:
1058        // CSecondaryAudioMediaType
1059                BOOL Initialize(const MF::CPresentationDescriptor& pPresentationDescriptor)
1060                {
1061                        DWORD nStreamCount;
1062                        __C(pPresentationDescriptor->GetStreamDescriptorCount(&nStreamCount));
1063                        for(DWORD nStreamIndex = 0; nStreamIndex < nStreamCount; nStreamIndex++)
1064                        {
1065                                BOOL bSelect;
1066                                MF::CStreamDescriptor pStreamDescriptor;
1067                                __C(pPresentationDescriptor->GetStreamDescriptorByIndex(nStreamIndex, &bSelect, &pStreamDescriptor));
1068                                pStreamDescriptor.Trace();
1069                                DWORD nStreamIdentifier;
1070                                __C(pStreamDescriptor->GetStreamIdentifier(&nStreamIdentifier));
1071                                CComPtr<IMFMediaTypeHandler> pMediaTypeHandler;
1072                                __C(pStreamDescriptor->GetMediaTypeHandler(&pMediaTypeHandler));
1073                                GUID MajorType;
1074                                __C(pMediaTypeHandler->GetMajorType(&MajorType));
1075                                _Z4(atlTraceGeneral, 4, _T("bSelect %d, nStreamIdentifier %d, MajorType %s\n"), bSelect, nStreamIdentifier, MF::FormatKey(MajorType));
1076                                if(MajorType != MFMediaType_Audio)
1077                                        continue;
1078                                DWORD nMediaTypeCount = 0;
1079                                __C(pMediaTypeHandler->GetMediaTypeCount(&nMediaTypeCount));
1080                                for(DWORD nMediaTypeIndex = 0; nMediaTypeIndex < nMediaTypeCount; nMediaTypeIndex++)
1081                                {
1082                                        MF::CMediaType pMediaType;
1083                                        __C(pMediaTypeHandler->GetMediaTypeByIndex(nMediaTypeIndex, &pMediaType));
1084                                        pMediaType.Trace();
1085                                        m_nStreamIndex = nStreamIndex;
1086                                        m_pMediaType = pMediaType;
1087                                        return TRUE;
1088                                }
1089                        }
1090                        return FALSE;
1091                }
1092        };
1093
1094public:
1095        static CEvent g_TerminationEvent;
1096        CString m_sVideoIdentifier;
1097        UINT32 m_nBitrate;
1098        CString m_sAudioIdentifier;
1099        UINT m_nDuration;
1100
1101public:
1102// CModule
1103        CModule()
1104        {
1105                AtlTraceSetDefaultSettings();
1106                _Z4_THIS();
1107                //_W(CExceptionFilter::Initialize());
1108                m_nBitrate = 0;
1109                m_nDuration = 10;
1110        }
1111        ~CModule()
1112        {
1113                _Z4_THIS();
1114                //CExceptionFilter::Terminate();
1115        }
1116        bool ParseCommandLine(LPCTSTR pszCommandLine, HRESULT* pnResult)
1117        {
1118                _A(pnResult);
1119                _ATLTRY
1120                {
1121                        CModule::CCommandLineArguments Arguments(pszCommandLine);
1122                        CModule::CCommandLineArguments::CArgument Argument;
1123                        while(Arguments.Next(Argument))
1124                        {
1125                                __D(!Argument.m_bSwitch, E_UNNAMED);
1126                                #pragma region help
1127                                if(Argument.m_sValue.CompareNoCase(_T("help")) == 0)
1128                                {
1129                                        _tprintf(
1130                                                _T("Syntax: %s argument [argument...]") _T("\n")
1131                                                _T("\n")
1132                                                _T("Arguments:") _T("\n")
1133                                                _T("\t") _T("help - displays syntax") _T("\n")
1134                                                _T("\t") _T("video <identifier> - use device with specific identifier for H.264 video capture") _T("\n"),
1135                                                _T("\t") _T("bitrate <bitrate> - override default video encoding bitrate") _T("\n"),
1136                                                _T("\t") _T("audio <identifier> - use device with specific identifier for audio capture") _T("\n"),
1137                                                _T("\t") _T("duration <duration> - use specific recording duration, in seconds, default is 10 seconds") _T("\n"),
1138                                                FindFileName(GetModulePath()));
1139                                        return false;
1140                                } else
1141                                #pragma endregion
1142                                #pragma region video
1143                                if(Argument.m_sValue.CompareNoCase(_T("video")) == 0)
1144                                {
1145                                        CModule::CCommandLineArguments::CArgument IdentifierArgument;
1146                                        __D(Arguments.Next(IdentifierArgument), E_UNNAMED);
1147                                        __D(!IdentifierArgument.m_bSwitch, E_UNNAMED);
1148                                        m_sVideoIdentifier = IdentifierArgument.m_sValue;
1149                                } else
1150                                #pragma endregion
1151                                #pragma region bitrate
1152                                if(Argument.m_sValue.CompareNoCase(_T("bitrate")) == 0)
1153                                {
1154                                        CModule::CCommandLineArguments::CArgument ValueArgument;
1155                                        __D(Arguments.Next(ValueArgument), E_UNNAMED);
1156                                        __D(!ValueArgument.m_bSwitch, E_UNNAMED);
1157                                        INT nBitrate;
1158                                        __D(AtlStringToInteger(ValueArgument.m_sValue, nBitrate), E_UNNAMED);
1159                                        m_nBitrate = (UINT32) nBitrate;
1160                                } else
1161                                #pragma endregion
1162                                #pragma region audio
1163                                if(Argument.m_sValue.CompareNoCase(_T("audio")) == 0)
1164                                {
1165                                        CModule::CCommandLineArguments::CArgument IdentifierArgument;
1166                                        __D(Arguments.Next(IdentifierArgument), E_UNNAMED);
1167                                        __D(!IdentifierArgument.m_bSwitch, E_UNNAMED);
1168                                        m_sAudioIdentifier = IdentifierArgument.m_sValue;
1169                                } else
1170                                #pragma endregion
1171                                #pragma region duration
1172                                if(Argument.m_sValue.CompareNoCase(_T("duration")) == 0)
1173                                {
1174                                        CModule::CCommandLineArguments::CArgument ValueArgument;
1175                                        __D(Arguments.Next(ValueArgument), E_UNNAMED);
1176                                        __D(!ValueArgument.m_bSwitch, E_UNNAMED);
1177                                        INT nDuration;
1178                                        __D(AtlStringToInteger(ValueArgument.m_sValue, nDuration), E_UNNAMED);
1179                                        m_nDuration = (UINT) nDuration;
1180                                } else
1181                                #pragma endregion
1182                                        __C(E_UNNAMED);
1183                        }
1184                }
1185                _ATLCATCH(Exception)
1186                {
1187                        *pnResult = Exception;
1188                        return false;
1189                }
1190                *pnResult = S_OK;
1191                return true;
1192        }
1193        HRESULT PreMessageLoop(INT nShowCommand)
1194        {
1195                const HRESULT nResult = __super::PreMessageLoop(nShowCommand);
1196                return SUCCEEDED(nResult) ? S_OK : nResult;
1197        }
1198        VOID EnumerateDevices()
1199        {
1200                _tprintf(_T("# Devices\n\n"));
1201                {
1202                        _tprintf(_T("## Video\n\n"));
1203                        MF::CAttributes pAttributes;
1204                        pAttributes.Create(1);
1205                        __C(pAttributes->SetGUID(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE, MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID));
1206                        MF::CActivateArray ActivateArray;
1207                        ActivateArray.EnumerateDeviceSources(pAttributes);
1208                        if(!ActivateArray.IsEmpty())
1209                        {
1210                                for(auto&& pActivate: ActivateArray)
1211                                {
1212                                        pActivate.Trace();
1213                                        const CString sIdentifier = pActivate.GetString(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK);
1214                                        const CString sFriendlyName = pActivate.GetString(MF_DEVSOURCE_ATTRIBUTE_FRIENDLY_NAME);
1215                                        _tprintf(_T(" * %s\n"), sFriendlyName);
1216                                        _tprintf(_T("  * Identifier: %s\n"), sIdentifier);
1217                                }
1218                                _tprintf(_T("\n"));
1219                        }
1220                }
1221                {
1222                        _tprintf(_T("## Audio\n\n"));
1223                        MF::CAttributes pAttributes;
1224                        pAttributes.Create(1);
1225                        __C(pAttributes->SetGUID(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE, MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_AUDCAP_GUID));
1226                        MF::CActivateArray ActivateArray;
1227                        ActivateArray.EnumerateDeviceSources(pAttributes);
1228                        if(!ActivateArray.IsEmpty())
1229                        {
1230                                for(auto&& pActivate: ActivateArray)
1231                                {
1232                                        pActivate.Trace();
1233                                        const CString sIdentifier = pActivate.GetString(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_AUDCAP_ENDPOINT_ID);
1234                                        const CString sFriendlyName = pActivate.GetString(MF_DEVSOURCE_ATTRIBUTE_FRIENDLY_NAME);
1235                                        _tprintf(_T(" * %s\n"), sFriendlyName);
1236                                        _tprintf(_T("  * Identifier: %s\n"), sIdentifier);
1237                                }
1238                                _tprintf(_T("\n"));
1239                        }
1240                }
1241        }
1242        static CPath CreateOutputPath()
1243        {
1244                CPath sPath = GetModulePath();
1245                sPath.RemoveExtension();
1246                SYSTEMTIME Time;
1247                GetLocalTime(&Time);
1248                return (LPCTSTR) AtlFormatString(_T("%s - %04d%02d%02d-%02d%02d%02d.mp4"), sPath, Time.wYear, Time.wMonth, Time.wDay, Time.wHour, Time.wMinute, Time.wSecond);
1249        }
1250        BOOL InternalHandlerRoutine(DWORD nType)
1251        {
1252                _W(g_TerminationEvent.Set());
1253                return TRUE;
1254        }
1255        static BOOL WINAPI HandlerRoutine(DWORD nType)
1256        {
1257                return static_cast<CModule*>(_pAtlModule)->InternalHandlerRoutine(nType);
1258        }
1259        VOID RunMessageLoop()
1260        {
1261                MF::CStartup Startup;
1262                // SUGG: Implement video only and audio only?
1263                if(m_sVideoIdentifier.IsEmpty() || m_sAudioIdentifier.IsEmpty())
1264                {
1265                        EnumerateDevices();
1266                        return;
1267                }
1268                CSessionT<CVideoMediaType, CAudioMediaType> Session;
1269                Session.Initialize(m_sVideoIdentifier, m_nBitrate, m_sAudioIdentifier);
1270                _tprintf(_T("Path: %s\n"), Session.m_sPath);
1271                CSessionT<CSecondaryVideoMediaType, CSecondaryAudioMediaType> SecondarySession;
1272//              SecondarySession.Initialize(
1273//                      _T("\\\\?\\usb#vid_1908&pid_2311&mi_00#6&3694d24c&1&0000#{e5323777-f976-4f5b-9b55-b94699c46e44}\\global"), 0, // USB2.0 PC CAMERA
1274//                      _T("{0.0.1.00000000}.{d7e99b5b-4d9b-437f-9f72-7adb7cdb2e1f}"), // Microphone (Realtek High Definition Audio)
1275//                      AtlFormatString(_T("%s - Secondary.mp4"), Session.m_sPath));
1276                if(SecondarySession.m_pSinkWriter)
1277                        _tprintf(_T("Secondary Path: %s\n"), SecondarySession.m_sPath);
1278                __C(Session.m_pSinkWriter->BeginWriting());
1279                if(SecondarySession.m_pSinkWriter)
1280                        __C(SecondarySession.m_pSinkWriter->BeginWriting());
1281                _ATLTRY
1282                {
1283                        __E(g_TerminationEvent.Create(NULL, TRUE, FALSE, NULL));
1284                        __E(SetConsoleCtrlHandler(&CModule::HandlerRoutine, TRUE));
1285                        _tprintf(_T("Starting capture, press Ctrl+Break to terminate\n"));
1286                        Session.m_pSourceReaderCallback->ReadNextSample(Session.m_VideoMediaType.m_nStreamIndex);
1287                        Session.m_pSourceReaderCallback->ReadNextSample(Session.m_AudioMediaType.m_nStreamIndex);
1288                        if(SecondarySession.m_pSourceReaderCallback)
1289                        {
1290                                SecondarySession.m_pSourceReaderCallback->ReadNextSample(SecondarySession.m_VideoMediaType.m_nStreamIndex);
1291                                SecondarySession.m_pSourceReaderCallback->ReadNextSample(SecondarySession.m_AudioMediaType.m_nStreamIndex);
1292                        }
1293                        const HANDLE phObjects[] = 
1294                        { 
1295                                g_TerminationEvent, 
1296                                Session.GetSampleAvailabilityEvent(),
1297                                SecondarySession.GetSampleAvailabilityEvent(),
1298                        };
1299                        DWORD nObjectCount = DIM(phObjects);
1300                        if(!phObjects[nObjectCount - 1])
1301                                nObjectCount--;
1302                        BOOL bSynchronized = FALSE;
1303                        for(; ; )
1304                        {
1305                                static const ULONG g_nTimeoutTime = 10 * 1000; // 10 seconds
1306                                const DWORD nWaitResult = WaitForMultipleObjects(nObjectCount, phObjects, FALSE, g_nTimeoutTime);
1307                                _Z5_WAITRESULT(nWaitResult);
1308                                if(nWaitResult == WAIT_OBJECT_0 + 0) // g_TerminationEvent
1309                                {
1310                                        _tprintf(_T("Finalizing output file...\n"));
1311                                        break;
1312                                }
1313                                __D(nWaitResult - WAIT_OBJECT_0 < DIM(phObjects), E_UNNAMED); // GetSampleAvailabilityEvent()
1314                                //if(!bSynchronized)
1315                                //{
1316                                //      LONGLONG nTime;
1317                                //      const SIZE_T nCount = Session.m_pSourceReaderCallback->GetLastListSampleTime(Session.m_VideoMediaType.m_nStreamIndex, nTime);
1318                                //      LONGLONG nSecondaryTime;
1319                                //      const SIZE_T nSecondaryCount = SecondarySession.m_pSourceReaderCallback->GetLastListSampleTime(SecondarySession.m_VideoMediaType.m_nStreamIndex, nSecondaryTime);
1320                                //      // NOTE: Basically we can sync them with any (first) frame but cameras tend to "warm up" (focus etc.) on start, so let's just skip a few...
1321                                //      if(nCount > 7 && nSecondaryCount > 7)
1322                                //      {
1323                                //              LONGLONG nMaximalTime = max(nTime, nSecondaryTime);
1324                                //              _tprintf(_T("Cameras synchronized, starting synchronized capture\n"));
1325                                //              Session.m_pSourceReaderCallback->ResetVideoCleanPointAvailable(Session.m_VideoMediaType.m_nStreamIndex, nMaximalTime - nTime);
1326                                //              SecondarySession.m_pSourceReaderCallback->ResetVideoCleanPointAvailable(SecondarySession.m_VideoMediaType.m_nStreamIndex, nMaximalTime - nSecondaryTime);
1327                                //              bSynchronized = TRUE;
1328                                //      }
1329                                //      // NOTE: It's okay that we did not start yet, but we need to reset the event to avoid false wake-ups
1330                                //      if(!bSynchronized)
1331                                //      {
1332                                //              _W(reinterpret_cast<CEvent&>(const_cast<HANDLE&>(phObjects[nWaitResult - WAIT_OBJECT_0])).Reset());
1333                                //              continue;
1334                                //      }
1335                                //}
1336                                if(nWaitResult == WAIT_OBJECT_0 + 1)
1337                                        Session.m_pSourceReaderCallback->WriteSamples(Session.m_pSinkWriter, Session.m_StreamIndexMap);
1338                                if(nWaitResult == WAIT_OBJECT_0 + 2)
1339                                        SecondarySession.m_pSourceReaderCallback->WriteSamples(SecondarySession.m_pSinkWriter, SecondarySession.m_StreamIndexMap);
1340                                // SUGG: Check both Session and SecondarySession
1341                                if(m_nDuration)
1342                                        if(Session.m_pSourceReaderCallback->GetLastSampleTime() >= m_nDuration * 1000 * 10000i64)
1343                                                break;
1344                        }
1345                }
1346                _ATLCATCHALL()
1347                {
1348                        _Z_EXCEPTION();
1349                        _tprintf(_T("An exception has been caught during processing, abnormal file finalization...\n"));
1350                }
1351                Session.m_pSourceReaderCallback->Terminate();
1352                if(SecondarySession.m_pSourceReaderCallback)
1353                        SecondarySession.m_pSourceReaderCallback->Terminate();
1354                // NOTE: This inernally calls media source Shutdown
1355                const HRESULT nFinalizeResult = Session.m_pSinkWriter->Finalize();
1356                _Z45_MFHRESULT(nFinalizeResult);
1357                _A(SUCCEEDED(nFinalizeResult));
1358                if(SecondarySession.m_pSinkWriter)
1359                {
1360                        const HRESULT nSecondaryFinalizeResult = SecondarySession.m_pSinkWriter->Finalize();
1361                        _Z45_MFHRESULT(nSecondaryFinalizeResult);
1362                        _A(SUCCEEDED(nSecondaryFinalizeResult));
1363                }
1364        }
1365};
1366
1367__declspec(selectany) CEvent CModule::g_TerminationEvent;
1368
1369/*
1370
1371video "\\?\usb#vid_046d&pid_0843&mi_00#6&2314864d&0&0000#{e5323777-f976-4f5b-9b55-b94699c46e44}\global" bitrate 1024000 audio "{0.0.1.00000000}.{d7e99b5b-4d9b-437f-9f72-7adb7cdb2e1f}"
1372video "\\?\usb#vid_046d&pid_0843&mi_00#6&2314864d&0&0000#{e5323777-f976-4f5b-9b55-b94699c46e44}\global" bitrate 2048000 audio "{0.0.1.00000000}.{a8bbaa1c-713d-4d6f-bf71-ae173bcc6dfc}" duration 15
1373
1374*/
Note: See TracBrowser for help on using the repository browser.