1// Copyright (c) Microsoft. All rights reserved.
2//
3// The MIT License (MIT)
4//
5// Permission is hereby granted, free of charge, to any person obtaining a copy
6// of this software and associated documentation files(the "Software"), to deal
7// in the Software without restriction, including without limitation the rights
8// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9// copies of the Software, and to permit persons to whom the Software is
10// furnished to do so, subject to the following conditions :
11//
12// The above copyright notice and this permission notice shall be included in
13// all copies or substantial portions of the Software.
14//
15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21// THE SOFTWARE.
22
23#include "MediaStreamSink.hpp"
24#include "MediaSink.hpp"
25#include "CaptureFrameGrabber.hpp"
26
27using namespace Media;
28using namespace Platform;
29using namespace Windows::Foundation;
30using namespace Windows::Media;
31using namespace Windows::Media::Capture;
32using namespace Windows::Media::MediaProperties;
33using namespace concurrency;
34using namespace Microsoft::WRL::Details;
35using namespace Microsoft::WRL;
36
37task<Media::CaptureFrameGrabber^> Media::CaptureFrameGrabber::CreateAsync(_In_ MediaCapture^ capture, _In_ VideoEncodingProperties^ props, CaptureStreamType streamType)
38{
39    auto reader = ref new Media::CaptureFrameGrabber(capture, props, streamType);
40
41    auto profile = ref new MediaEncodingProfile();
42    profile->Video = props;
43
44    task<void> task;
45    if (reader->_streamType == CaptureStreamType::Preview)
46    {
47        task = create_task(capture->StartPreviewToCustomSinkAsync(profile, reader->_mediaExtension));
48    }
49    else
50    {
51        task = create_task(capture->StartRecordToCustomSinkAsync(profile, reader->_mediaExtension));
52    }
53
54    return task.then([reader]()
55    {
56        reader->_state = State::Started;
57        return reader;
58    });
59}
60
61Media::CaptureFrameGrabber::CaptureFrameGrabber(_In_ MediaCapture^ capture, _In_ VideoEncodingProperties^ props, CaptureStreamType streamType)
62: _state(State::Created)
63, _streamType(streamType)
64, _capture(capture)
65{
66    auto videoSampleHandler = ref new MediaSampleHandler(this, &Media::CaptureFrameGrabber::ProcessSample);
67
68    _mediaSink = Make<MediaSink>(nullptr, props, nullptr, videoSampleHandler);
69    _mediaExtension = reinterpret_cast<IMediaExtension^>(static_cast<AWM::IMediaExtension*>(_mediaSink.Get()));
70}
71
72Media::CaptureFrameGrabber::~CaptureFrameGrabber()
73{
74    if (_state == State::Started)
75    {
76        if (_streamType == CaptureStreamType::Preview)
77        {
78            (void)_capture->StopPreviewAsync();
79        }
80        else
81        {
82            (void)_capture->StopRecordAsync();
83        }
84    }
85
86    if (_mediaSink != nullptr)
87    {
88        (void)_mediaSink->Shutdown();
89        _mediaSink = nullptr;
90    }
91    _mediaExtension = nullptr;
92    _capture = nullptr;
93}
94
95void Media::CaptureFrameGrabber::ShowCameraSettings()
96{
97#if WINAPI_FAMILY!=WINAPI_FAMILY_PHONE_APP
98    if (_state == State::Started)
99    {
100        CameraOptionsUI::Show(_capture.Get());
101    }
102#endif
103}
104
105task<void> Media::CaptureFrameGrabber::FinishAsync()
106{
107    auto lock = _lock.LockExclusive();
108
109    if (_state != State::Started)
110    {
111        throw ref new COMException(E_UNEXPECTED, L"State");
112    }
113    _state = State::Closing;
114
115    if (_mediaSink != nullptr)
116    {
117        (void)_mediaSink->Shutdown();
118        _mediaSink = nullptr;
119    }
120    _mediaExtension = nullptr;
121
122    task<void> task;
123    if (_streamType == CaptureStreamType::Preview)
124    {
125        task = create_task(_capture->StopPreviewAsync());
126    }
127    else
128    {
129        task = create_task(_capture->StopRecordAsync());
130    }
131
132    return task.then([this]()
133    {
134        auto lock = _lock.LockExclusive();
135        _state = State::Closed;
136        _capture = nullptr;
137    });
138}
139
140task<ComPtr<IMF2DBuffer2>> Media::CaptureFrameGrabber::GetFrameAsync()
141{
142    auto lock = _lock.LockExclusive();
143
144    if (_state != State::Started)
145    {
146        throw ref new COMException(E_UNEXPECTED, L"State");
147    }
148
149    _mediaSink->RequestVideoSample();
150
151    task_completion_event<ComPtr<IMF2DBuffer2>> taskEvent;
152    _videoSampleRequestQueue.push(taskEvent);
153
154    return create_task(taskEvent);
155}
156
157void Media::CaptureFrameGrabber::ProcessSample(_In_ MediaSample^ sample)
158{
159    task_completion_event<ComPtr<IMF2DBuffer2>> t;
160
161    {
162        auto lock = _lock.LockExclusive();
163
164        t = _videoSampleRequestQueue.front();
165        _videoSampleRequestQueue.pop();
166    }
167
168    ComPtr<IMFMediaBuffer> buffer;
169    CHK(sample->Sample->ConvertToContiguousBuffer(&buffer));
170
171    // Dispatch without the lock taken to avoid deadlocks
172    t.set(As<IMF2DBuffer2>(buffer));
173}