framework2
1384 строки · 45.5 Кб
1
2#include "ofPixels.h"
3#include "ofMediaFoundationPlayer.h"
4#include "ofLog.h"
5#include <string.h>
6#include <mfapi.h>
7#include <mferror.h>
8#include "ofTexture.h"
9#include "ofGLUtils.h"
10#include "ofGraphics.h"
11#include "ofEventUtils.h"
12
13// declares some shared Media Foundation code
14#include "ofMediaFoundationSoundPlayer.h"
15
16using namespace Microsoft::WRL;
17
18std::shared_ptr<ofMediaFoundationPlayer::MEDXDeviceManager> ofMediaFoundationPlayer::sDeviceManager;
19
20bool ofMediaFoundationPlayer::sBAllowDurationHack = true;
21
22//----------------------------------------------
23void ofMediaFoundationPlayer::setDurationHackEnabled(bool ab) {
24sBAllowDurationHack = ab;
25}
26
27//----------------------------------------------
28ofMediaFoundationPlayer::MEDXDeviceManager::MEDXDeviceManager() {
29gl_handleD3D = nullptr;
30}
31
32//----------------------------------------------
33ofMediaFoundationPlayer::MEDXDeviceManager::~MEDXDeviceManager() {
34if (gl_handleD3D != nullptr) {
35ofLogVerbose("ofMEDXDeviceManager") << " closing gl handleD3D.";
36wglDXCloseDeviceNV(gl_handleD3D);
37gl_handleD3D = nullptr;
38}
39mBUseDX = false;
40m_spDXGIManager = nullptr;
41}
42
43//----------------------------------------------
44bool ofMediaFoundationPlayer::MEDXDeviceManager::createDX11Device() {
45static const D3D_FEATURE_LEVEL levels[] = {
46D3D_FEATURE_LEVEL_11_1,
47D3D_FEATURE_LEVEL_11_0,
48D3D_FEATURE_LEVEL_10_1,
49D3D_FEATURE_LEVEL_10_0,
50D3D_FEATURE_LEVEL_9_3,
51D3D_FEATURE_LEVEL_9_2,
52D3D_FEATURE_LEVEL_9_1
53};
54
55D3D_FEATURE_LEVEL FeatureLevel;
56HRESULT hr = S_OK;
57
58mBUseDX = true;
59
60hr = D3D11CreateDevice(
61nullptr,
62D3D_DRIVER_TYPE_HARDWARE,
63nullptr,
64D3D11_CREATE_DEVICE_VIDEO_SUPPORT | D3D11_CREATE_DEVICE_BGRA_SUPPORT,
65levels,
66ARRAYSIZE(levels),
67D3D11_SDK_VERSION,
68&m_spDX11Device,
69&FeatureLevel,
70&m_spDX11DeviceContext
71);
72
73if (FAILED(hr)) {
74ofLogError("ofMEDXDeviceManager::CreateDX11Device()") << " unable to use hw accel.";
75mBUseDX = false;
76return mBUseDX;
77}
78
79ComPtr<ID3D10Multithread> spMultithread;
80if (SUCCEEDED(m_spDX11Device.Get()->QueryInterface(IID_PPV_ARGS(&spMultithread)))) {
81spMultithread->SetMultithreadProtected(TRUE);
82} else {
83ofLogError("ofMEDXDeviceManager :: CreateDX11Device") << " unable to set multi thread.";
84mBUseDX = false;
85return mBUseDX;
86}
87
88
89hr = MFCreateDXGIDeviceManager(&mResetToken, &m_spDXGIManager);
90if (FAILED(hr)) {
91ofLogError("ofMEDXDeviceManager :: CreateDX11Device") << " unable to create DXGIDeviceManager.";
92mBUseDX = false;
93return mBUseDX;
94}
95
96hr = m_spDXGIManager->ResetDevice(m_spDX11Device.Get(), mResetToken);
97if (FAILED(hr)) {
98ofLogError("ofMEDXDeviceManager :: CreateDX11Device") << " unable to ResetDevice.";
99mBUseDX = false;
100return mBUseDX;
101}
102
103if (mBUseDX) {
104if (SUCCEEDED(hr)) {
105gl_handleD3D = wglDXOpenDeviceNV(m_spDX11Device.Get());
106}
107}
108
109if (gl_handleD3D == nullptr) {
110ofLogError("ofMEDXDeviceManager :: CreateDX11Device") << " error creating GL D3D Handle.";
111mBUseDX = false;
112}
113
114return mBUseDX;
115}
116
117class BstrURL {
118public:
119BstrURL(std::string aurl) {
120std::wstring ws = std::wstring(aurl.begin(), aurl.end());
121assert(!ws.empty());
122_bstrStr = SysAllocStringLen(ws.data(), ws.size());
123}
124~BstrURL() {
125SysReleaseString(_bstrStr);
126_bstrStr = nullptr;
127}
128
129operator BSTR() const {
130return _bstrStr;
131}
132private:
133BSTR _bstrStr = nullptr;
134};
135
136//----------------------------------------------
137bool ofMediaFoundationPlayer::METexture::allocate( ofPixelFormat afmt, int aw, int ah) {
138if (mOfTex && ((int)mOfTex->getWidth() != aw || (int)mOfTex->getHeight() != ah)) {
139mOfTex.reset();
140}
141if (!mOfTex) {
142mOfTex = std::make_shared<ofTexture>();
143}
144mWidth = aw;
145mHeight = ah;
146mOfPixFmt = afmt;
147//auto glFormat = ofGetGLInternalFormatFromPixelFormat(OF_PIXELS_BGRA);
148auto glFormat = ofGetGLInternalFormatFromPixelFormat(afmt);
149// make a GL_TEXTURE2D
150mOfTex->allocate(mWidth, mHeight, glFormat, false);
151return true;
152}
153
154//----------------------------------------------
155bool ofMediaFoundationPlayer::METexture::_swapPixelsFromSrc4ChannelTo3(ofPixels& aDstPix) {
156const auto targetPixFormat = aDstPix.getPixelFormat();
157const auto srcPixFormat = mSrcPixels.getPixelFormat();
158
159bool bNeedsSwap = (srcPixFormat == OF_PIXELS_BGRA && targetPixFormat == OF_PIXELS_RGB);
160if (srcPixFormat == OF_PIXELS_RGBA && targetPixFormat == OF_PIXELS_BGR) {
161bNeedsSwap = true;
162}
163
164if (bNeedsSwap) {
165auto srcPixels = mSrcPixels.getPixelsIter();
166auto dstPixels = aDstPix.getPixelsIter();
167auto srcPixel = srcPixels.begin();
168auto dstPixel = dstPixels.begin();
169auto endPixel = srcPixels.end();
170for (; srcPixel != srcPixels.end(); srcPixel++, dstPixel++) {
171dstPixel[0] = srcPixel[2];
172dstPixel[1] = srcPixel[1];
173dstPixel[2] = srcPixel[0];
174}
175} else {
176// straight copy, removing the alpha channel
177auto srcPixels = mSrcPixels.getPixelsIter();
178auto dstPixels = aDstPix.getPixelsIter();
179auto srcPixel = srcPixels.begin();
180auto dstPixel = dstPixels.begin();
181auto endPixel = srcPixels.end();
182for (; srcPixel != srcPixels.end(); srcPixel++, dstPixel++) {
183dstPixel[0] = srcPixel[0];
184dstPixel[1] = srcPixel[1];
185dstPixel[2] = srcPixel[2];
186}
187}
188return true;
189}
190
191//----------------------------------------------
192class SharedDXGLTexture : public ofMediaFoundationPlayer::METexture {
193public:
194SharedDXGLTexture() { mGLDX_Handle = nullptr; }
195~SharedDXGLTexture();
196
197bool allocate(ofPixelFormat afmt, int aw, int ah);
198bool create(DXGI_FORMAT aDxFormat) override;
199bool transferFrame(IMFMediaEngine* aengine) override;
200bool isValid() override { return mBValid; }
201HANDLE getGLDXHandle() { return mGLDX_Handle; }
202ID3D11Texture2D* getDXTexture() { return mDXTex.Get(); }
203
204bool draw(ofPixels& apix) override;
205bool updatePixels(ofTexture& aSrcTex, ofPixels& apix, ofPixelFormat aTargetPixFormat) override;
206
207bool lock();
208bool unlock();
209bool isLocked();
210
211protected:
212ComPtr<ID3D11Texture2D> mDXTex{ nullptr };
213ComPtr<ID3D11Texture2D> stagingTexture{ nullptr };
214bool mBValid = false;
215HANDLE mGLDX_Handle;
216bool mBLocked = false;
217};
218
219//----------------------------------------------
220class WICTextureManager : public ofMediaFoundationPlayer::METexture {
221public:
222bool isValid() override { return mBValid; }
223
224bool allocate(ofPixelFormat afmt, int aw, int ah) override;
225bool create(DXGI_FORMAT aDxFormat) override;
226
227bool transferFrame(IMFMediaEngine* aengine) override;
228bool draw(ofPixels& apix) override;
229bool updatePixels(ofTexture& aSrcTex, ofPixels& apix, ofPixelFormat aTargetPixFormat) override;
230
231protected:
232Microsoft::WRL::ComPtr<IWICBitmap> mWicBitmap = nullptr;
233Microsoft::WRL::ComPtr<IWICImagingFactory> mWicFactory = nullptr;
234bool mBValid = false;
235};
236
237//----------------------------------------------
238bool SharedDXGLTexture::allocate(ofPixelFormat afmt, int aw, int ah) {
239ofPixelFormat outfmt = OF_PIXELS_BGRA;
240if (afmt == OF_PIXELS_RGBA || afmt == OF_PIXELS_RGB) {
241outfmt = OF_PIXELS_RGBA;
242}
243return METexture::allocate(outfmt, aw, ah);
244}
245
246//----------------------------------------------
247bool SharedDXGLTexture::create(DXGI_FORMAT aDxFormat) {
248unsigned int tw = static_cast<unsigned int>(getWidth());
249unsigned int th = static_cast<unsigned int>(getHeight());
250
251D3D11_TEXTURE2D_DESC desc = {};
252desc.Width = tw;
253desc.Height = th;
254desc.MipLevels = 1;
255desc.ArraySize = 1;
256desc.Format = aDxFormat;
257desc.SampleDesc.Count = 1;
258desc.SampleDesc.Quality = 0;
259desc.BindFlags = D3D11_BIND_RENDER_TARGET;
260desc.Usage = D3D11_USAGE_DEFAULT;
261
262auto dxMan = ofMediaFoundationPlayer::getDxDeviceManager();
263
264if (SUCCEEDED(dxMan->getD11Device()->CreateTexture2D(&desc, nullptr, mDXTex.GetAddressOf()))) {
265mGLDX_Handle = wglDXRegisterObjectNV(
266dxMan->getGLHandleD3D(),
267mDXTex.Get(),
268mOfTex->getTextureData().textureID,
269GL_TEXTURE_2D,
270WGL_ACCESS_READ_ONLY_NV);
271
272D3D11_TEXTURE2D_DESC desc2;
273desc2.Width = desc.Width;
274desc2.Height = desc.Height;
275desc2.MipLevels = desc.MipLevels;
276desc2.ArraySize = desc.ArraySize;
277desc2.Format = desc.Format;
278desc2.SampleDesc = desc.SampleDesc;
279desc2.Usage = D3D11_USAGE_STAGING;
280desc2.BindFlags = 0;
281desc2.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
282desc2.MiscFlags = 0;
283
284HRESULT hr = dxMan->getD11Device()->CreateTexture2D(&desc2, nullptr, stagingTexture.GetAddressOf());
285if (FAILED(hr)) {
286ofLogError("ofMEVideoPlayer :: SharedDXGLTexture :: create") << " Failed to create staging texture";
287return false;
288}
289
290mBValid = (mGLDX_Handle != nullptr);
291} else {
292ofLogError("SharedDXGLTexture :: createSharedTexture") << " ERROR Creating shared texture.";
293mBValid = false;
294}
295return mBValid;
296}
297
298//----------------------------------------------
299bool SharedDXGLTexture::transferFrame(IMFMediaEngine* aengine) {
300if (!mBValid || !mGLDX_Handle) {
301return false;
302}
303RECT targetRect{ 0, 0, mWidth, mHeight };
304if ((aengine->TransferVideoFrame(
305getDXTexture(),
306&mNormalizedVidRect, &targetRect, &bgColor)) == S_OK) {
307return true;
308}
309return false;
310}
311
312//----------------------------------------------
313bool SharedDXGLTexture::lock() {
314if (mBLocked) return false;
315if (!mBValid) return false;
316mBLocked = wglDXLockObjectsNV(ofMediaFoundationPlayer::getDxDeviceManager()->getGLHandleD3D(), 1, &mGLDX_Handle );
317return mBLocked;
318}
319
320//----------------------------------------------
321bool SharedDXGLTexture::unlock() {
322if (!mBLocked) return false;
323if (wglDXUnlockObjectsNV(ofMediaFoundationPlayer::getDxDeviceManager()->getGLHandleD3D(), 1, &mGLDX_Handle )) {
324mBLocked = false;
325return true;
326}
327return false;
328}
329
330//----------------------------------------------
331bool SharedDXGLTexture::isLocked() {
332return mBLocked;
333}
334
335//----------------------------------------------
336bool SharedDXGLTexture::draw(ofPixels& apix) {
337if (lock()) {
338mOfTex->draw(0, 0);
339unlock();
340return true;
341}
342return false;
343}
344
345//----------------------------------------------
346bool SharedDXGLTexture::updatePixels(ofTexture& aSrcTex, ofPixels& apix, ofPixelFormat aTargetPixFormat) {
347
348auto deviceMan = ofMediaFoundationPlayer::getDxDeviceManager();
349auto immediateContext = deviceMan->getContext();
350ID3D11Texture2D* lDestImage = getDXTexture();
351auto d3device = deviceMan->getD11Device();
352
353// Copy GPU Resource to CPU
354D3D11_TEXTURE2D_DESC desc;
355lDestImage->GetDesc(&desc);
356D3D11_MAPPED_SUBRESOURCE mapInfo;
357
358HRESULT hr = S_OK;
359immediateContext->CopyResource(stagingTexture.Get(), lDestImage);
360// copy the texture to a staging resource
361if (!stagingTexture) {
362ofLogError("ofMediaFoundationPlayer :: SharedDXGLTexture :: updatePixels") << " ERROR copying staging texture.";
363return false;
364}
365
366// now, map the staging resource
367hr = immediateContext->Map(
368stagingTexture.Get(),
3690,
370D3D11_MAP_READ,
3710,
372&mapInfo);
373if (hr != S_OK) {
374ofLogError("ofMediaFoundationPlayer :: SharedDXGLTexture :: updatePixels") << " Failed to map staging texture.";
375return false;
376}
377immediateContext->Unmap(stagingTexture.Get(), 0);
378
379if (FAILED(hr)) {
380ofLogVerbose("ofMediaFoundationPlayer :: SharedDXGLTexture :: updatePixels") << " unable to map hw dx texture.";
381aSrcTex.readToPixels(apix);
382return apix.getWidth() > 0;
383}
384// the mOfPixFmt is always going to be BGRA || RGBA
385bool bSetStraightOnPix = (mOfPixFmt == aTargetPixFormat);
386if (mOfPixFmt == OF_PIXELS_BGRA && aTargetPixFormat == OF_PIXELS_RGBA) {
387bSetStraightOnPix = true;
388}
389if (mOfPixFmt == OF_PIXELS_RGBA && aTargetPixFormat == OF_PIXELS_BGRA) {
390bSetStraightOnPix = true;
391}
392if (bSetStraightOnPix) {
393apix.setFromPixels(reinterpret_cast<unsigned char*>(mapInfo.pData), getWidth(), getHeight(), mOfPixFmt);
394if (aTargetPixFormat != mOfPixFmt) {
395apix.swapRgb();
396}
397} else {
398mSrcPixels.setFromPixels(reinterpret_cast<unsigned char*>(mapInfo.pData), getWidth(), getHeight(), mOfPixFmt);
399
400apix.allocate(mSrcPixels.getWidth(), mSrcPixels.getHeight(), aTargetPixFormat);
401_swapPixelsFromSrc4ChannelTo3(apix);
402}
403return apix.getWidth() > 0 && apix.getHeight() > 0;
404}
405
406//----------------------------------------------
407SharedDXGLTexture::~SharedDXGLTexture() {
408// release the handle
409if (mGLDX_Handle != nullptr) {
410if (wglGetCurrentContext() != nullptr) {
411if (isLocked()) {
412unlock();
413}
414wglDXUnregisterObjectNV(ofMediaFoundationPlayer::getDxDeviceManager()->getGLHandleD3D(), mGLDX_Handle);
415mGLDX_Handle = nullptr;
416}
417}
418}
419
420//----------------------------------------------
421bool WICTextureManager::allocate(ofPixelFormat afmt, int aw, int ah) {
422ofLogVerbose("ofMediaFoundationVideoPlayer :: WICTextureManager") << " allocate.";
423METexture::allocate(OF_PIXELS_BGRA, aw, ah);
424mBValid = false;
425HRESULT hr = CoCreateInstance(CLSID_WICImagingFactory, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&mWicFactory));
426if (hr == S_OK && mWicFactory) {
427mBValid = true;
428ofLogVerbose("ofMediaFoundationVideoPlayer :: WICTextureManager") << " created CLSID_WICImagingFactory.";
429} else {
430ofLogError("ofMediaFoundationVideoPlayer :: WICTextureManager") << " CLSID_WICImagingFactory.";
431mWicFactory = nullptr;
432}
433return mBValid;
434}
435
436//----------------------------------------------
437bool WICTextureManager::create(DXGI_FORMAT aDxFormat) {
438
439if (mBValid && mWicFactory) {
440unsigned int tw = static_cast<unsigned int>(getWidth());
441unsigned int th = static_cast<unsigned int>(getHeight());
442
443if (!mWicBitmap || mWidth != tw || mHeight != th) {
444if (mWicBitmap) {
445mWicBitmap->Release();
446}
447mWidth = tw;
448mHeight = th;
449
450GUID wicPixFmt = GUID_WICPixelFormat32bppBGRA;
451//GUID_WICPixelFormat32bppRGBA
452HRESULT hr = mWicFactory->CreateBitmap(tw, th, wicPixFmt, WICBitmapCacheOnDemand,
453mWicBitmap.GetAddressOf());
454if (hr == S_OK) {
455
456} else {
457ofLogError("ofMediaFoundationVideoPlayer :: WICTextureManager") << " ERROR CreateBitmap.";
458mBValid = false;
459mWicBitmap = nullptr;
460}
461}
462}
463
464return mBValid;
465}
466
467//----------------------------------------------
468bool WICTextureManager::transferFrame(IMFMediaEngine* aengine) {
469if (!mBValid || !mWicBitmap) {
470return false;
471}
472RECT targetRect{ 0, 0, mWidth, mHeight };
473if ((aengine->TransferVideoFrame(
474mWicBitmap.Get(),
475&mNormalizedVidRect, &targetRect, &bgColor)) == S_OK) {
476return true;
477}
478return false;
479}
480
481//----------------------------------------------
482bool WICTextureManager::draw(ofPixels& apix) {
483
484ComPtr<IWICBitmapLock> lockedData;
485DWORD flags = WICBitmapLockRead;
486WICRect srcRect{ 0, 0, mWidth, mHeight };
487
488if (FAILED(mWicBitmap->Lock(&srcRect, flags, lockedData.GetAddressOf()))) {
489return false;
490}
491
492UINT stride{ 0 };
493if (FAILED(lockedData->GetStride(&stride))) {
494return false;
495}
496
497UINT bufferSize{ 0 };
498unsigned char* data{ nullptr };
499if (FAILED(lockedData->GetDataPointer(&bufferSize, &data))) {
500return false;
501}
502
503mSrcPixels.setFromAlignedPixels(data, getWidth(), getHeight(), mOfPixFmt, stride);
504mOfTex->loadData(apix);
505//mOfTex->loadData(data, mWidth, mHeight, GL_BGRA);
506mOfTex->draw(0, 0);
507
508lockedData->Release();
509
510return true;
511}
512
513//----------------------------------------------
514bool WICTextureManager::updatePixels(ofTexture& aSrcTex, ofPixels& apix, ofPixelFormat aTargetPixFormat) {
515// always has mOfPixFmt == OF_PIXELS_BGRA
516bool bSetStraightOnPix = (mOfPixFmt == aTargetPixFormat);
517if (mOfPixFmt == OF_PIXELS_BGRA && aTargetPixFormat == OF_PIXELS_RGBA) {
518bSetStraightOnPix = true;
519}
520if (bSetStraightOnPix) {
521apix = mSrcPixels;
522if (aTargetPixFormat != mOfPixFmt) {
523apix.swapRgb();
524}
525} else {
526// swap around pixels
527apix.allocate(mSrcPixels.getWidth(), mSrcPixels.getHeight(), aTargetPixFormat);
528_swapPixelsFromSrc4ChannelTo3(apix);
529}
530return true;
531}
532
533//----------------------------------------------
534std::string ofMediaFoundationPlayer::MFEventToString(MF_MEDIA_ENGINE_EVENT aevent) {
535static std::unordered_map<MF_MEDIA_ENGINE_EVENT, std::string> sMFMessages =
536{
537{ MF_MEDIA_ENGINE_EVENT_LOADSTART, "MF_MEDIA_ENGINE_EVENT_LOADSTART" },
538{ MF_MEDIA_ENGINE_EVENT_PROGRESS, "MF_MEDIA_ENGINE_EVENT_PROGRESS" },
539{ MF_MEDIA_ENGINE_EVENT_SUSPEND, "MF_MEDIA_ENGINE_EVENT_SUSPEND" },
540{ MF_MEDIA_ENGINE_EVENT_ABORT, "MF_MEDIA_ENGINE_EVENT_ABORT" },
541{ MF_MEDIA_ENGINE_EVENT_ERROR, "MF_MEDIA_ENGINE_EVENT_ERROR" },
542{ MF_MEDIA_ENGINE_EVENT_EMPTIED, "MF_MEDIA_ENGINE_EVENT_EMPTIED" },
543{ MF_MEDIA_ENGINE_EVENT_STALLED, "MF_MEDIA_ENGINE_EVENT_STALLED" },
544{ MF_MEDIA_ENGINE_EVENT_PLAY, "MF_MEDIA_ENGINE_EVENT_PLAY" },
545{ MF_MEDIA_ENGINE_EVENT_PAUSE, "MF_MEDIA_ENGINE_EVENT_PAUSE" },
546{ MF_MEDIA_ENGINE_EVENT_LOADEDMETADATA, "MF_MEDIA_ENGINE_EVENT_LOADEDMETADATA" },
547{ MF_MEDIA_ENGINE_EVENT_LOADEDDATA, "MF_MEDIA_ENGINE_EVENT_LOADEDDATA" },
548{ MF_MEDIA_ENGINE_EVENT_WAITING, "MF_MEDIA_ENGINE_EVENT_WAITING" },
549{ MF_MEDIA_ENGINE_EVENT_PLAYING, "MF_MEDIA_ENGINE_EVENT_PLAYING" },
550{ MF_MEDIA_ENGINE_EVENT_CANPLAY, "MF_MEDIA_ENGINE_EVENT_CANPLAY" },
551{ MF_MEDIA_ENGINE_EVENT_CANPLAYTHROUGH, "MF_MEDIA_ENGINE_EVENT_CANPLAYTHROUGH" },
552{ MF_MEDIA_ENGINE_EVENT_SEEKING, "MF_MEDIA_ENGINE_EVENT_SEEKING" },
553{ MF_MEDIA_ENGINE_EVENT_SEEKED, "MF_MEDIA_ENGINE_EVENT_SEEKED" },
554{ MF_MEDIA_ENGINE_EVENT_TIMEUPDATE, "MF_MEDIA_ENGINE_EVENT_TIMEUPDATE" },
555{ MF_MEDIA_ENGINE_EVENT_ENDED, "MF_MEDIA_ENGINE_EVENT_ENDED" },
556{ MF_MEDIA_ENGINE_EVENT_RATECHANGE, "MF_MEDIA_ENGINE_EVENT_RATECHANGE" },
557{ MF_MEDIA_ENGINE_EVENT_DURATIONCHANGE, "MF_MEDIA_ENGINE_EVENT_DURATIONCHANGE" },
558{ MF_MEDIA_ENGINE_EVENT_VOLUMECHANGE, "MF_MEDIA_ENGINE_EVENT_VOLUMECHANGE" },
559{ MF_MEDIA_ENGINE_EVENT_FORMATCHANGE, "MF_MEDIA_ENGINE_EVENT_FORMATCHANGE" },
560{ MF_MEDIA_ENGINE_EVENT_PURGEQUEUEDEVENTS, "MF_MEDIA_ENGINE_EVENT_PURGEQUEUEDEVENTS" },
561{ MF_MEDIA_ENGINE_EVENT_TIMELINE_MARKER, "MF_MEDIA_ENGINE_EVENT_TIMELINE_MARKER" },
562{ MF_MEDIA_ENGINE_EVENT_BALANCECHANGE, "MF_MEDIA_ENGINE_EVENT_BALANCECHANGE" },
563{ MF_MEDIA_ENGINE_EVENT_DOWNLOADCOMPLETE, "MF_MEDIA_ENGINE_EVENT_DOWNLOADCOMPLETE" },
564{ MF_MEDIA_ENGINE_EVENT_BUFFERINGSTARTED, "MF_MEDIA_ENGINE_EVENT_BUFFERINGSTARTED" },
565{ MF_MEDIA_ENGINE_EVENT_BUFFERINGENDED, "MF_MEDIA_ENGINE_EVENT_BUFFERINGENDED" },
566{ MF_MEDIA_ENGINE_EVENT_FRAMESTEPCOMPLETED, "MF_MEDIA_ENGINE_EVENT_FRAMESTEPCOMPLETED" },
567{ MF_MEDIA_ENGINE_EVENT_NOTIFYSTABLESTATE, "MF_MEDIA_ENGINE_EVENT_NOTIFYSTABLESTATE" },
568{ MF_MEDIA_ENGINE_EVENT_FIRSTFRAMEREADY, "MF_MEDIA_ENGINE_EVENT_FIRSTFRAMEREADY" },
569{ MF_MEDIA_ENGINE_EVENT_TRACKSCHANGE, "MF_MEDIA_ENGINE_EVENT_TRACKSCHANGE" },
570{ MF_MEDIA_ENGINE_EVENT_OPMINFO, "MF_MEDIA_ENGINE_EVENT_OPMINFO" },
571{ MF_MEDIA_ENGINE_EVENT_RESOURCELOST, "MF_MEDIA_ENGINE_EVENT_RESOURCELOST" },
572{ MF_MEDIA_ENGINE_EVENT_DELAYLOADEVENT_CHANGED, "MF_MEDIA_ENGINE_EVENT_DELAYLOADEVENT_CHANGED" },
573{ MF_MEDIA_ENGINE_EVENT_STREAMRENDERINGERROR, "MF_MEDIA_ENGINE_EVENT_STREAMRENDERINGERROR" },
574{ MF_MEDIA_ENGINE_EVENT_SUPPORTEDRATES_CHANGED, "MF_MEDIA_ENGINE_EVENT_SUPPORTEDRATES_CHANGED" },
575{ MF_MEDIA_ENGINE_EVENT_AUDIOENDPOINTCHANGE, "MF_MEDIA_ENGINE_EVENT_AUDIOENDPOINTCHANGE" }
576};
577
578if (sMFMessages.count(aevent) > 0) {
579return sMFMessages.at(aevent);
580}
581return std::to_string(aevent);
582}
583
584//----------------------------------------------
585std::string ofMediaFoundationPlayer::MFErrorToString(MF_MEDIA_ENGINE_ERR aerror) {
586static std::unordered_map<MF_MEDIA_ENGINE_ERR, std::string> sMFErrorMessages =
587{
588{MF_MEDIA_ENGINE_ERR_NOERROR, "MF_MEDIA_ENGINE_ERR_NOERROR" },
589{MF_MEDIA_ENGINE_ERR_ABORTED, "MF_MEDIA_ENGINE_ERR_ABORTED"},
590{MF_MEDIA_ENGINE_ERR_NETWORK, "MF_MEDIA_ENGINE_ERR_NETWORK"},
591{MF_MEDIA_ENGINE_ERR_DECODE, "MF_MEDIA_ENGINE_ERR_DECODE"},
592{MF_MEDIA_ENGINE_ERR_SRC_NOT_SUPPORTED, "MF_MEDIA_ENGINE_ERR_SRC_NOT_SUPPORTED"},
593{MF_MEDIA_ENGINE_ERR_ENCRYPTED, "MF_MEDIA_ENGINE_ERR_ENCRYPTED"}
594};
595
596if (sMFErrorMessages.count(aerror) > 0) {
597return sMFErrorMessages.at(aerror);
598}
599
600return std::to_string(aerror);
601}
602
603//----------------------------------------------
604ofMediaFoundationPlayer::ofMediaFoundationPlayer() {
605//sInitMediaFoundation();
606// TODO: this is currently located in ofMediaFoundationSoundPlayer
607ofMediaFoundationUtils::InitMediaFoundation();
608InitializeCriticalSectionEx(&m_critSec, 0, 0);
609mPixFormat = OF_PIXELS_RGB;
610}
611
612//----------------------------------------------
613ofMediaFoundationPlayer::~ofMediaFoundationPlayer() {
614close();
615ofMediaFoundationUtils::CloseMediaFoundation();
616DeleteCriticalSection(&m_critSec);
617}
618
619//----------------------------------------------
620std::shared_ptr<ofMediaFoundationPlayer::MEDXDeviceManager> ofMediaFoundationPlayer::getDxDeviceManager() {
621if (!sDeviceManager) {
622sDeviceManager = std::make_shared<MEDXDeviceManager>();
623sDeviceManager->createDX11Device();
624}
625return sDeviceManager;
626}
627
628//----------------------------------------------
629bool ofMediaFoundationPlayer::load(std::string name) {
630return _load(name, false);
631}
632
633//----------------------------------------------
634void ofMediaFoundationPlayer::loadAsync(std::string name) {
635_load(name, true);
636}
637
638//----------------------------------------------
639bool ofMediaFoundationPlayer::_load(std::string name, bool abAsync) {
640close();
641
642mBLoadAsync = abAsync;
643
644bool bStream = false;
645bStream = bStream || ofIsStringInString(name, "http://");
646bStream = bStream || ofIsStringInString(name, "https://");
647bStream = bStream || ofIsStringInString(name, "rtsp://");
648bStream = bStream || ofIsStringInString(name, "rtmp://");
649
650std::string absPath = name;
651
652if (!bStream) {
653if (ofFile::doesFileExist(absPath)) {
654absPath = ofFilePath::getAbsolutePath(absPath, true);
655} else {
656ofLogError("ofMediaFoundationPlayer") << " file does not exist! " << absPath;
657return false;
658}
659}
660
661
662EnterCriticalSection(&m_critSec);
663
664// init device manager if not created
665if (isUsingHWAccel()) {
666setUsingHWAccel(getDxDeviceManager()->isHWSupported());
667}
668
669if (isUsingHWAccel()) {
670ofLogVerbose("ofMediaFoundationPlayer::load") << " utilizing hardware acceleration.";
671} else {
672ofLogVerbose("ofMediaFoundationPlayer::load") << " utilizing software decoding.";
673}
674
675
676ComPtr<IMFMediaEngineClassFactory> spFactory;
677ComPtr<IMFAttributes> spAttributes;
678
679HRESULT hr = S_OK;
680
681hr = CoCreateInstance(CLSID_MFMediaEngineClassFactory, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&spFactory));
682
683if (FAILED(hr)) {
684ofLogError("ofMediaFoundationPlayer::load") << " unable to create Media Engine Class Factory.";
685}
686if (SUCCEEDED(hr)) {
687mEventProcessor = std::make_shared<ofMEEventProcessor>();
688mEventProcessor->setCB(this);
689hr = MFCreateAttributes(&spAttributes, 1);
690if (FAILED(hr)) {
691ofLogError("ofMediaFoundationPlayer::load") << " unable to MFCreateAttributes.";
692}
693}
694
695if (SUCCEEDED(hr)) {
696if (isUsingHWAccel()) {
697hr = spAttributes->SetUnknown(MF_MEDIA_ENGINE_DXGI_MANAGER, (IUnknown*)getDxDeviceManager()->getDXGIManagerPtr());
698if (FAILED(hr)) {
699ofLogError("ofMediaFoundationPlayer::load") << " unable to set device Manager.";
700}
701}
702}
703
704if (SUCCEEDED(hr)) {
705hr = spAttributes->SetUnknown(MF_MEDIA_ENGINE_CALLBACK, (IUnknown*)mEventProcessor.get());
706if (FAILED(hr)) {
707ofLogError("ofMediaFoundationPlayer::load") << " unable to set media engine callback.";
708}
709}
710
711if (SUCCEEDED(hr)) {
712hr = spAttributes->SetUINT32(MF_MEDIA_ENGINE_VIDEO_OUTPUT_FORMAT, m_d3dFormat);
713if (FAILED(hr)) {
714ofLogError("ofMediaFoundationPlayer::load") << " unable to set Video Output Format.";
715}
716}
717
718if (SUCCEEDED(hr)) {
719//const DWORD flags = MF_MEDIA_ENGINE_WAITFORSTABLE_STATE;
720const DWORD flags = MF_MEDIA_ENGINE_REAL_TIME_MODE;
721hr = spFactory->CreateInstance(flags, spAttributes.Get(), &m_spMediaEngine);
722if (FAILED(hr)) {
723ofLogError("ofMediaFoundationPlayer::load") << " unable to create media engine.";
724}
725}
726
727m_spMediaEngine->SetAutoPlay(FALSE);
728
729// now lets make a BSTR
730m_spMediaEngine->SetSource(BstrURL(absPath));
731
732hr = m_spMediaEngine->Load();
733
734//mBLoaded = (hr == S_OK);
735
736if (hr == S_OK) {
737HRESULT hr2 = m_spMediaEngine.Get()->QueryInterface(__uuidof(IMFMediaEngine), (void**)&m_spEngineEx);
738if (FAILED(hr2)) {
739ofLogError("ofMediaFoundationPlayer::load") << " unable to create media engine ex.";
740}
741}
742
743LeaveCriticalSection(&m_critSec);
744
745if (hr != S_OK) {
746close();
747}
748
749if (!mBLoadAsync) {
750if (hr == S_OK) {
751mBIsDoneAtomic.store(false);
752mBIsClosedAtomic.store(false);
753
754std::mutex lock;
755std::unique_lock lk{ lock };
756mWaitCondition.wait(lk, [&] { return mBIsDoneAtomic.load() || mBIsClosedAtomic.load(); });
757}
758}
759
760//mBLoaded = SUCCEEDED(hr);
761
762return (hr == S_OK);
763}
764
765
766
767//----------------------------------------------
768void ofMediaFoundationPlayer::close() {
769
770mBIsClosedAtomic.store(true);
771mBIsDoneAtomic.store(false);
772mWaitCondition.notify_all();
773
774EnterCriticalSection(&m_critSec);
775
776if (m_spMediaEngine) {
777ofMediaFoundationUtils::CallAsyncBlocking(
778[&] { m_spMediaEngine->Shutdown(); }
779);
780}
781
782m_spMediaEngine = nullptr;
783
784// clear out the events
785{
786std::unique_lock<std::mutex> lk(mMutexEvents);
787if (!mEventsQueue.empty()) {
788std::queue<DWORD> tempty;
789std::swap(mEventsQueue, tempty);
790}
791}
792
793mTargetSeekPercent = -1.0f;
794mBLoaded = false;
795mBNewFrame = false;
796mDuration = 0.f;
797mWidth = 0.f;
798mHeight = 0.f;
799mBReady = false;
800mBDone = false;
801mBPlaying = false;
802mBCanSeek = false;
803mFramerate = 1.f / 30.f;
804mEstimatedNumFrames = 1;
805mBUpdatePixels = false;
806
807if (mMeTexture) {
808mMeTexture.reset();
809}
810
811if (mEventProcessor) {
812mEventProcessor.reset();
813}
814
815mFbo.clear();
816
817LeaveCriticalSection(&m_critSec);
818
819}
820
821//----------------------------------------------
822bool ofMediaFoundationPlayer::isInitialized() const {
823return mBReady;
824}
825
826//----------------------------------------------
827void ofMediaFoundationPlayer::OnMediaEngineEvent(DWORD aEvent, DWORD_PTR param1, DWORD param2) {
828if (aEvent == MF_MEDIA_ENGINE_EVENT_LOADEDMETADATA) {
829if (!mBLoadAsync) {
830mBIsDoneAtomic.store(true);
831mWaitCondition.notify_one();
832}
833mBLoaded = true;
834} else if (aEvent == MF_MEDIA_ENGINE_EVENT_ERROR) {
835// not sure if we should handle this here
836ofLogVerbose("engine event: ofMediaFoundationPlayer") << " ERRROR";
837// clear out the mutex so that we no longer wait and the close event can be fired
838if (!mBLoadAsync) {
839mBIsDoneAtomic.store(false);
840mBIsClosedAtomic.store(true);
841mWaitCondition.notify_one();
842}
843}
844// lets not overload the events, query similar with isFrameNew()
845if (aEvent != MF_MEDIA_ENGINE_EVENT_TIMEUPDATE) {
846std::unique_lock<std::mutex> tt(mMutexEvents);
847mEventsQueue.push(aEvent);
848}
849
850
851}
852
853//----------------------------------------------
854void ofMediaFoundationPlayer::update() {
855mBNewFrame = false;
856mBDone = false;
857if (mBLoaded && hasVideo() ) {
858LONGLONG time;
859if(m_spMediaEngine->OnVideoStreamTick(&time) == S_OK) {
860if (mMeTexture && mMeTexture->isValid()) {
861if ( mMeTexture->transferFrame(m_spMediaEngine.Get())) {
862ofPushStyle(); {
863ofSetColor(255);
864
865bool bValidDraw = false;
866mFbo.begin(); {
867bValidDraw = mMeTexture->draw(mPixels);
868} mFbo.end();
869
870if (bValidDraw) {
871mCopyTex = mFbo.getTexture();
872if (mBUpdatePixels) {
873mMeTexture->updatePixels(mCopyTex, mPixels,mPixFormat);
874}
875mBNewFrame = true;
876}
877} ofPopStyle();
878}
879}
880}
881}
882
883if (m_spMediaEngine) {
884if (!mBLoaded) {
885mTargetSeekPercent = -1.f;
886}
887if (mTargetSeekPercent > -0.5f && isInitialized()) {
888setPosition(mTargetSeekPercent);
889}
890}
891
892if (ofMediaFoundationPlayer::sBAllowDurationHack && m_spMediaEngine) {
893if (mBLoaded) {
894//not sure why the GetDuration() method returns inaccurate values,
895// but this will update the duration if the current time is larger
896double ctime = m_spMediaEngine->GetCurrentTime();
897if (ctime > mDuration) {
898mDuration = ctime;
899if (mFramerate > 0.0) {
900mEstimatedNumFrames = mDuration / (1.f / mFramerate);
901}
902}
903}
904if (mBNewFrame) {
905if (mFramerate > 0.0) {
906mEstimatedNumFrames = mDuration / (1.f / mFramerate);
907}
908}
909}
910
911unsigned int mMaxEventsToProcess = 1000;
912unsigned int numEventsProcessed = 0;
913// now lets update the events in the queue
914bool bHasEvent = true;
915DWORD tevent;
916while (bHasEvent && (numEventsProcessed < mMaxEventsToProcess) ) {
917bHasEvent = false;
918{
919std::unique_lock<std::mutex> lk(mMutexEvents);
920if (!mEventsQueue.empty()) {
921tevent = mEventsQueue.front();
922mEventsQueue.pop();
923bHasEvent = true;
924}
925}
926if (bHasEvent) {
927// handle the event //
928handleMEEvent(tevent);
929numEventsProcessed++;
930}
931}
932}
933
934//----------------------------------------------
935bool ofMediaFoundationPlayer::isFrameNew() const {
936return mBNewFrame;
937}
938
939//----------------------------------------------
940void ofMediaFoundationPlayer::play() {
941if (m_spMediaEngine) {
942if (mBDone) {
943setPosition(0.f);
944}
945m_spMediaEngine->Play();
946mBDone = false;
947}
948}
949
950//----------------------------------------------
951void ofMediaFoundationPlayer::stop() {
952if (m_spMediaEngine) {
953if (isPlaying()) {
954setPosition(0.f);
955setPaused(true);
956mBDone = false;
957}
958}
959}
960
961//----------------------------------------------
962void ofMediaFoundationPlayer::setPaused(bool bPause) {
963if (m_spMediaEngine) {
964if (bPause) {
965m_spMediaEngine->Pause();
966mBPlaying = false;
967} else {
968play();
969}
970}
971}
972
973//----------------------------------------------
974bool ofMediaFoundationPlayer::isLoaded() const {
975return mBLoaded;
976}
977
978//----------------------------------------------
979bool ofMediaFoundationPlayer::isPlaying() const {
980return mBPlaying;
981}
982
983//----------------------------------------------
984float ofMediaFoundationPlayer::getWidth() const {
985return mWidth;
986}
987
988//----------------------------------------------
989float ofMediaFoundationPlayer::getHeight() const {
990return mHeight;
991}
992
993//----------------------------------------------
994ofTexture* ofMediaFoundationPlayer::getTexturePtr() {
995return &mCopyTex;
996}
997
998//----------------------------------------------
999bool ofMediaFoundationPlayer::isPaused() const {
1000if (m_spMediaEngine) {
1001return m_spMediaEngine->IsPaused();
1002}
1003return false;
1004}
1005
1006//----------------------------------------------
1007void ofMediaFoundationPlayer::setPosition(float pct) {
1008if (m_spMediaEngine) {
1009if (!mBCanSeek) {
1010ofLogError("ofMediaFoundationPlayer :: setPosition") << " seeking is not supported.";
1011return;
1012}
1013
1014if (mDuration > 0.0 && isInitialized() && pct >= 0.f && pct <= 1.0f) {
1015// does not like when we are seeking //
1016double ttime = static_cast<double>(pct) * mDuration;
1017if (ttime >= mDuration) {
1018ttime = mDuration;
1019}
1020if (ttime < 0.0) {
1021ttime = 0.0;
1022}
1023if (!m_spMediaEngine->IsSeeking()) {
1024if (m_spEngineEx) {
1025// MF_MEDIA_ENGINE_SEEK_MODE_APPROXIMATE
1026m_spEngineEx->SetCurrentTimeEx(ttime, MF_MEDIA_ENGINE_SEEK_MODE_NORMAL);
1027} else if (m_spMediaEngine) {
1028m_spMediaEngine->SetCurrentTime(ttime);
1029}
1030mTargetSeekPercent = -1.f;
1031//callAsync([&] {m_spMediaEngine->SetCurrentTime(ttime); });
1032} else {
1033mTargetSeekPercent = pct;
1034}
1035}
1036}
1037}
1038
1039//----------------------------------------------
1040void ofMediaFoundationPlayer::setSpeed(float speed) {
1041if (m_spMediaEngine && m_spEngineEx) {
1042if (m_spEngineEx->IsPlaybackRateSupported(static_cast<double>(speed))) {
1043HRESULT hr = m_spMediaEngine->SetPlaybackRate(static_cast<double>(speed));
1044if (hr != S_OK) {
1045ofLogVerbose("ofMediaFoundationPlayer :: setSpeed : Unable to set speed to ") << speed << ".";
1046}
1047}
1048}
1049}
1050
1051//----------------------------------------------
1052void ofMediaFoundationPlayer::setVolume(float volume) {
1053if (m_spMediaEngine) {
1054ofMediaFoundationUtils::CallAsyncBlocking(
1055[&] {m_spMediaEngine->SetVolume(static_cast<double>(volume));
1056});
1057}
1058}
1059
1060//----------------------------------------------
1061void ofMediaFoundationPlayer::setFrame(int frame) {
1062setPosition((float)frame / (float)getTotalNumFrames());
1063}
1064
1065//----------------------------------------------
1066int ofMediaFoundationPlayer::getCurrentFrame() const {
1067return getPosition() * mEstimatedNumFrames;
1068}
1069
1070//----------------------------------------------
1071int ofMediaFoundationPlayer::getTotalNumFrames() const {
1072return mEstimatedNumFrames;
1073}
1074
1075//----------------------------------------------
1076void ofMediaFoundationPlayer::setLoopState(ofLoopType state) {
1077if (state == OF_LOOP_NONE || state == OF_LOOP_NORMAL ) {
1078if (m_spMediaEngine) {
1079BOOL loop = (state == OF_LOOP_NORMAL) ? TRUE : FALSE;
1080m_spMediaEngine->SetLoop( loop );
1081}
1082mLoopType = state;
1083} else {
1084ofLogError("ofMediaFoundationPlayer") << " cannot set loop of type palindrome.";
1085}
1086}
1087
1088//----------------------------------------------
1089ofLoopType ofMediaFoundationPlayer::getLoopState() const {
1090return mLoopType;
1091}
1092
1093//----------------------------------------------
1094float ofMediaFoundationPlayer::getPosition() const {
1095if (m_spMediaEngine && mDuration > 0.0 ) {
1096//return static_cast<float>(m_spMediaEngine->GetCurrentTime()) / mDuration;
1097const double ctime = m_spMediaEngine->GetCurrentTime();
1098if (ctime > mDuration) {
1099mDuration = ctime;
1100}
1101return (ctime) / mDuration;
1102}
1103return 0.f;
1104}
1105
1106//----------------------------------------------
1107float ofMediaFoundationPlayer::getSpeed() const {
1108if (m_spMediaEngine) {
1109return static_cast<float>(m_spMediaEngine->GetPlaybackRate());
1110}
1111return 1.f;
1112}
1113
1114//----------------------------------------------
1115float ofMediaFoundationPlayer::getDuration() const {
1116return mDuration;
1117}
1118
1119//----------------------------------------------
1120bool ofMediaFoundationPlayer::getIsMovieDone() const {
1121return mBDone;
1122}
1123
1124//----------------------------------------------
1125bool ofMediaFoundationPlayer::hasAudio() {
1126if (m_spMediaEngine) {
1127return m_spMediaEngine->HasAudio();
1128}
1129return false;
1130}
1131
1132//----------------------------------------------
1133bool ofMediaFoundationPlayer::hasVideo() {
1134if (m_spMediaEngine) {
1135return m_spMediaEngine->HasVideo();
1136}
1137return false;
1138}
1139
1140//----------------------------------------------
1141void ofMediaFoundationPlayer::firstFrame() {
1142setPosition(0.0f);
1143}
1144
1145//----------------------------------------------
1146void ofMediaFoundationPlayer::nextFrame() {
1147if (m_spEngineEx) {
1148m_spEngineEx->FrameStep(TRUE);
1149}
1150}
1151
1152//----------------------------------------------
1153void ofMediaFoundationPlayer::previousFrame() {
1154if (m_spEngineEx) {
1155m_spEngineEx->FrameStep(FALSE);
1156}
1157}
1158
1159//----------------------------------------------
1160bool ofMediaFoundationPlayer::setPixelFormat(ofPixelFormat pixelFormat) {
1161if (pixelFormat == OF_PIXELS_BGRA || pixelFormat == OF_PIXELS_BGR ) {
1162m_d3dFormat = DXGI_FORMAT_B8G8R8A8_UNORM;
1163} else if(pixelFormat == OF_PIXELS_RGBA || pixelFormat == OF_PIXELS_RGB ) {
1164m_d3dFormat = DXGI_FORMAT_R8G8B8A8_UNORM;
1165}
1166
1167switch (pixelFormat) {
1168case OF_PIXELS_RGB:
1169case OF_PIXELS_BGR:
1170case OF_PIXELS_BGRA:
1171case OF_PIXELS_RGBA:
1172mPixFormat = pixelFormat;
1173return true;
1174default:
1175return false;
1176}
1177return false;
1178}
1179
1180//----------------------------------------------
1181ofPixelFormat ofMediaFoundationPlayer::getPixelFormat() const {
1182return mPixFormat;
1183}
1184
1185//----------------------------------------------
1186ofPixels& ofMediaFoundationPlayer::getPixels() {
1187if (!mBUpdatePixels && mFbo.isAllocated()) {
1188mFbo.readToPixels(mPixels);
1189}
1190mBUpdatePixels = true;
1191return mPixels;
1192}
1193
1194//----------------------------------------------
1195const ofPixels& ofMediaFoundationPlayer::getPixels() const {
1196mBUpdatePixels = true;
1197return mPixels;
1198}
1199
1200//----------------------------------------------
1201void ofMediaFoundationPlayer::handleMEEvent(DWORD aevent) {
1202if (aevent != MF_MEDIA_ENGINE_EVENT_TIMEUPDATE) {
1203ofLogVerbose("ofMediaFoundationPlayer") << MFEventToString(static_cast<MF_MEDIA_ENGINE_EVENT>(aevent));
1204}
1205
1206switch (aevent) {
1207case MF_MEDIA_ENGINE_EVENT_LOADEDMETADATA:
1208{
1209//mDuration = static_cast<float>(m_spMediaEngine->GetDuration());
1210updateDuration();
1211if (mDuration != mDuration || mDuration == std::numeric_limits<float>::infinity()) {
1212mDuration = 0.f;
1213} else {
1214DWORD caps = 0;
1215if (m_spEngineEx) {
1216m_spEngineEx->GetResourceCharacteristics(&caps);
1217mBCanSeek = (caps & MFMEDIASOURCE_CAN_SEEK) > 0;
1218}
1219}
1220mBDone = false;
1221mWidth = 0.f;
1222mHeight = 0.f;
1223DWORD w, h;
1224if (SUCCEEDED(m_spMediaEngine->GetNativeVideoSize(&w, &h))) {
1225mWidth = w;
1226mHeight = h;
1227
1228if (mMeTexture) {
1229if (mMeTexture->getWidth() != mWidth || mMeTexture->getHeight() != mHeight) {
1230mMeTexture.reset();
1231mFbo.clear();
1232}
1233}
1234
1235if (!mMeTexture) {
1236if (isUsingHWAccel()) {
1237ofLogVerbose(" ofMediaFoundationPlayer::handleMEEvent") << " creating a shared texture that is hw supported.";
1238mMeTexture = std::make_shared<SharedDXGLTexture>();
1239} else {
1240ofLogVerbose(" ofMediaFoundationPlayer::handleMEEvent") << " creating a WIC Texture manager.";
1241mMeTexture = std::make_shared<WICTextureManager>();
1242}
1243mMeTexture->allocate(mPixFormat, getWidth(), getHeight());
1244mMeTexture->create(m_d3dFormat);
1245
1246ofFbo::Settings fsettings;
1247fsettings.internalformat = ofGetGLInternalFormatFromPixelFormat(mPixFormat);
1248fsettings.useDepth = false;
1249fsettings.useStencil = false;
1250fsettings.width = mWidth;
1251fsettings.height = mHeight;
1252fsettings.numSamples = 0;
1253mFbo.allocate(fsettings);
1254mFbo.begin(); {
1255ofClear(0, 0, 0, 255);
1256} mFbo.end();
1257mCopyTex = mFbo.getTexture();
1258}
1259}
1260// in case this was called before load //
1261setLoopState(getLoopState());
1262//mBReady = true;
1263mBLoaded = true;
1264break;
1265}
1266case MF_MEDIA_ENGINE_EVENT_LOADEDDATA:
1267{
1268//IMFMediaEngineEx::GetStreamAttribute
1269//HRESULT GetStreamAttribute(
1270// [in] DWORD dwStreamIndex,
1271// [in] REFGUID guidMFAttribute,
1272// [out] PROPVARIANT * pvValue
1273//);
1274// MF_MT_FRAME_RATE
1275DWORD nstreams;
1276
1277if (m_spEngineEx && SUCCEEDED(m_spEngineEx->GetNumberOfStreams(&nstreams)) ) {
1278if (nstreams > 0) {
1279
1280//MF_MT_FRAME_RATE {UINT64 (HI32(Numerator),LO32(Denominator))}
1281{
1282PROPVARIANT pvar;
1283HRESULT hr = m_spEngineEx->GetStreamAttribute(0, MF_MT_FRAME_RATE, &pvar);
1284if (hr == S_OK) {
1285auto numerator = pvar.hVal.HighPart / pvar.hVal.LowPart;
1286auto denom = pvar.uhVal.HighPart / pvar.uhVal.LowPart;
1287mFramerate = (static_cast<float>(pvar.hVal.HighPart) / static_cast<float>(pvar.hVal.LowPart));
1288} else {
1289mFramerate = 1.f / 30.f;
1290}
1291PropVariantClear(&pvar);
1292}
1293updateDuration();
1294}
1295}
1296
1297//mBReady = true;
1298break;
1299}
1300case MF_MEDIA_ENGINE_EVENT_CANPLAY:
1301{
1302// Start the Playback
1303//play();
1304//stop();
1305break;
1306}
1307case MF_MEDIA_ENGINE_EVENT_PLAY:
1308{
1309mBPlaying = true;
1310break;
1311}
1312case MF_MEDIA_ENGINE_EVENT_PAUSE:
1313{
1314mBPlaying = false;
1315break;
1316}
1317case MF_MEDIA_ENGINE_EVENT_ENDED:
1318{
1319mBDone = true;
1320break;
1321}
1322case MF_MEDIA_ENGINE_EVENT_TIMEUPDATE:
1323{
1324break;
1325}
1326case MF_MEDIA_ENGINE_EVENT_SEEKING:
1327{
1328mTimeStartedSeek = m_spMediaEngine->GetCurrentTime();
1329break;
1330}
1331case MF_MEDIA_ENGINE_EVENT_SEEKED:
1332{
1333auto ctime = m_spMediaEngine->GetCurrentTime();
1334// looping videos don't fire off an ended event, so try here
1335if (ctime < 0.1 && (ctime - mTimeStartedSeek < 0.05)) {
1336mBDone = true;
1337}
1338
1339break;
1340}
1341case MF_MEDIA_ENGINE_EVENT_ERROR:
1342{
1343if (m_spMediaEngine) {
1344ComPtr<IMFMediaError> error;
1345if (m_spMediaEngine->GetError(&error) == S_OK) {
1346USHORT errorCode = error->GetErrorCode();
1347MF_MEDIA_ENGINE_ERR meError = static_cast<MF_MEDIA_ENGINE_ERR>(errorCode);
1348ofLogError("ofMediaFoundationPlayer") << MFErrorToString(meError);
1349ofNotifyEvent(MFErrorEvent, meError, this);
1350close();
1351}
1352}
1353break;
1354
1355}
1356case MF_MEDIA_ENGINE_EVENT_DURATIONCHANGE:
1357{
1358updateDuration();
1359break;
1360}
1361case MF_MEDIA_ENGINE_EVENT_FIRSTFRAMEREADY:
1362{
1363mBReady = true;
1364}
1365}
1366MF_MEDIA_ENGINE_EVENT mfEvent = static_cast<MF_MEDIA_ENGINE_EVENT>(aevent);
1367ofNotifyEvent(MFEngineEvent, mfEvent, this);
1368
1369}
1370
1371//-----------------------------------------
1372void ofMediaFoundationPlayer::updateDuration() {
1373if (m_spMediaEngine) {
1374mDuration = (m_spMediaEngine->GetDuration());
1375ofLogVerbose("ofMediaFoundationPlayer") << " update duration: " << mDuration;
1376if (mDuration != mDuration || mDuration == std::numeric_limits<double>::infinity()) {
1377mDuration = 0.0;
1378}
1379if (mDuration > 0.0 && mFramerate > 0.f) {
1380mEstimatedNumFrames = mDuration / (1.f / mFramerate);
1381} else {
1382mEstimatedNumFrames = 1;
1383}
1384}
1385}
1386
1387