Alexandre Lision | 8af73cb | 2013-12-10 14:11:20 -0500 | [diff] [blame] | 1 | //------------------------------------------------------------------------------
|
| 2 | // File: AMFilter.cpp
|
| 3 | //
|
| 4 | // Desc: DirectShow base classes - implements class hierarchy for streams
|
| 5 | // architecture.
|
| 6 | //
|
| 7 | // Copyright (c) 1992-2001 Microsoft Corporation. All rights reserved.
|
| 8 | //------------------------------------------------------------------------------
|
| 9 |
|
| 10 |
|
| 11 | //=====================================================================
|
| 12 | //=====================================================================
|
| 13 | // The following classes are declared in this header:
|
| 14 | //
|
| 15 | //
|
| 16 | // CBaseMediaFilter Basic IMediaFilter support (abstract class)
|
| 17 | // CBaseFilter Support for IBaseFilter (incl. IMediaFilter)
|
| 18 | // CEnumPins Enumerate input and output pins
|
| 19 | // CEnumMediaTypes Enumerate the preferred pin formats
|
| 20 | // CBasePin Abstract base class for IPin interface
|
| 21 | // CBaseOutputPin Adds data provider member functions
|
| 22 | // CBaseInputPin Implements IMemInputPin interface
|
| 23 | // CMediaSample Basic transport unit for IMemInputPin
|
| 24 | // CBaseAllocator General list guff for most allocators
|
| 25 | // CMemAllocator Implements memory buffer allocation
|
| 26 | //
|
| 27 | //=====================================================================
|
| 28 | //=====================================================================
|
| 29 |
|
| 30 | #include <pjmedia-videodev/config.h>
|
| 31 |
|
| 32 | #if defined(PJMEDIA_VIDEO_DEV_HAS_DSHOW) && PJMEDIA_VIDEO_DEV_HAS_DSHOW != 0
|
| 33 |
|
| 34 | #include <streams.h>
|
| 35 | #include <strsafe.h>
|
| 36 |
|
| 37 | #ifdef DXMPERF
|
| 38 | #include "dxmperf.h"
|
| 39 | #endif // DXMPERF
|
| 40 |
|
| 41 |
|
| 42 | //=====================================================================
|
| 43 | // Helpers
|
| 44 | //=====================================================================
|
| 45 | STDAPI CreateMemoryAllocator(__deref_out IMemAllocator **ppAllocator)
|
| 46 | {
|
| 47 | return CoCreateInstance(CLSID_MemoryAllocator,
|
| 48 | 0,
|
| 49 | CLSCTX_INPROC_SERVER,
|
| 50 | IID_IMemAllocator,
|
| 51 | (void **)ppAllocator);
|
| 52 | }
|
| 53 |
|
| 54 | // Put this one here rather than in ctlutil.cpp to avoid linking
|
| 55 | // anything brought in by ctlutil.cpp
|
| 56 | STDAPI CreatePosPassThru(
|
| 57 | __in_opt LPUNKNOWN pAgg,
|
| 58 | BOOL bRenderer,
|
| 59 | IPin *pPin,
|
| 60 | __deref_out IUnknown **ppPassThru
|
| 61 | )
|
| 62 | {
|
| 63 | *ppPassThru = NULL;
|
| 64 | IUnknown *pUnkSeek;
|
| 65 | HRESULT hr = CoCreateInstance(CLSID_SeekingPassThru,
|
| 66 | pAgg,
|
| 67 | CLSCTX_INPROC_SERVER,
|
| 68 | IID_IUnknown,
|
| 69 | (void **)&pUnkSeek
|
| 70 | );
|
| 71 | if (FAILED(hr)) {
|
| 72 | return hr;
|
| 73 | }
|
| 74 |
|
| 75 | ISeekingPassThru *pPassThru;
|
| 76 | hr = pUnkSeek->QueryInterface(IID_ISeekingPassThru, (void**)&pPassThru);
|
| 77 | if (FAILED(hr)) {
|
| 78 | pUnkSeek->Release();
|
| 79 | return hr;
|
| 80 | }
|
| 81 | hr = pPassThru->Init(bRenderer, pPin);
|
| 82 | pPassThru->Release();
|
| 83 | if (FAILED(hr)) {
|
| 84 | pUnkSeek->Release();
|
| 85 | return hr;
|
| 86 | }
|
| 87 | *ppPassThru = pUnkSeek;
|
| 88 | return S_OK;
|
| 89 | }
|
| 90 |
|
| 91 |
|
| 92 |
|
| 93 | #define CONNECT_TRACE_LEVEL 3
|
| 94 |
|
| 95 | //=====================================================================
|
| 96 | //=====================================================================
|
| 97 | // Implements CBaseMediaFilter
|
| 98 | //=====================================================================
|
| 99 | //=====================================================================
|
| 100 |
|
| 101 |
|
| 102 | /* Constructor */
|
| 103 |
|
| 104 | CBaseMediaFilter::CBaseMediaFilter(__in_opt LPCTSTR pName,
|
| 105 | __inout_opt LPUNKNOWN pUnk,
|
| 106 | __in CCritSec *pLock,
|
| 107 | REFCLSID clsid) :
|
| 108 | CUnknown(pName, pUnk),
|
| 109 | m_pLock(pLock),
|
| 110 | m_clsid(clsid),
|
| 111 | m_State(State_Stopped),
|
| 112 | m_pClock(NULL)
|
| 113 | {
|
| 114 | }
|
| 115 |
|
| 116 |
|
| 117 | /* Destructor */
|
| 118 |
|
| 119 | CBaseMediaFilter::~CBaseMediaFilter()
|
| 120 | {
|
| 121 | // must be stopped, but can't call Stop here since
|
| 122 | // our critsec has been destroyed.
|
| 123 |
|
| 124 | /* Release any clock we were using */
|
| 125 |
|
| 126 | if (m_pClock) {
|
| 127 | m_pClock->Release();
|
| 128 | m_pClock = NULL;
|
| 129 | }
|
| 130 | }
|
| 131 |
|
| 132 |
|
| 133 | /* Override this to say what interfaces we support and where */
|
| 134 |
|
| 135 | STDMETHODIMP
|
| 136 | CBaseMediaFilter::NonDelegatingQueryInterface(
|
| 137 | REFIID riid,
|
| 138 | __deref_out void ** ppv)
|
| 139 | {
|
| 140 | if (riid == IID_IMediaFilter) {
|
| 141 | return GetInterface((IMediaFilter *) this, ppv);
|
| 142 | } else if (riid == IID_IPersist) {
|
| 143 | return GetInterface((IPersist *) this, ppv);
|
| 144 | } else {
|
| 145 | return CUnknown::NonDelegatingQueryInterface(riid, ppv);
|
| 146 | }
|
| 147 | }
|
| 148 |
|
| 149 | /* Return the filter's clsid */
|
| 150 | STDMETHODIMP
|
| 151 | CBaseMediaFilter::GetClassID(__out CLSID *pClsID)
|
| 152 | {
|
| 153 | CheckPointer(pClsID,E_POINTER);
|
| 154 | ValidateReadWritePtr(pClsID,sizeof(CLSID));
|
| 155 | *pClsID = m_clsid;
|
| 156 | return NOERROR;
|
| 157 | }
|
| 158 |
|
| 159 | /* Override this if your state changes are not done synchronously */
|
| 160 |
|
| 161 | STDMETHODIMP
|
| 162 | CBaseMediaFilter::GetState(DWORD dwMSecs, __out FILTER_STATE *State)
|
| 163 | {
|
| 164 | UNREFERENCED_PARAMETER(dwMSecs);
|
| 165 | CheckPointer(State,E_POINTER);
|
| 166 | ValidateReadWritePtr(State,sizeof(FILTER_STATE));
|
| 167 |
|
| 168 | *State = m_State;
|
| 169 | return S_OK;
|
| 170 | }
|
| 171 |
|
| 172 |
|
| 173 | /* Set the clock we will use for synchronisation */
|
| 174 |
|
| 175 | STDMETHODIMP
|
| 176 | CBaseMediaFilter::SetSyncSource(__inout_opt IReferenceClock *pClock)
|
| 177 | {
|
| 178 | CAutoLock cObjectLock(m_pLock);
|
| 179 |
|
| 180 | // Ensure the new one does not go away - even if the same as the old
|
| 181 | if (pClock) {
|
| 182 | pClock->AddRef();
|
| 183 | }
|
| 184 |
|
| 185 | // if we have a clock, release it
|
| 186 | if (m_pClock) {
|
| 187 | m_pClock->Release();
|
| 188 | }
|
| 189 |
|
| 190 | // Set the new reference clock (might be NULL)
|
| 191 | // Should we query it to ensure it is a clock? Consider for a debug build.
|
| 192 | m_pClock = pClock;
|
| 193 |
|
| 194 | return NOERROR;
|
| 195 | }
|
| 196 |
|
| 197 | /* Return the clock we are using for synchronisation */
|
| 198 | STDMETHODIMP
|
| 199 | CBaseMediaFilter::GetSyncSource(__deref_out_opt IReferenceClock **pClock)
|
| 200 | {
|
| 201 | CheckPointer(pClock,E_POINTER);
|
| 202 | ValidateReadWritePtr(pClock,sizeof(IReferenceClock *));
|
| 203 | CAutoLock cObjectLock(m_pLock);
|
| 204 |
|
| 205 | if (m_pClock) {
|
| 206 | // returning an interface... addref it...
|
| 207 | m_pClock->AddRef();
|
| 208 | }
|
| 209 | *pClock = (IReferenceClock*)m_pClock;
|
| 210 | return NOERROR;
|
| 211 | }
|
| 212 |
|
| 213 |
|
| 214 | /* Put the filter into a stopped state */
|
| 215 |
|
| 216 | STDMETHODIMP
|
| 217 | CBaseMediaFilter::Stop()
|
| 218 | {
|
| 219 | CAutoLock cObjectLock(m_pLock);
|
| 220 |
|
| 221 | m_State = State_Stopped;
|
| 222 | return S_OK;
|
| 223 | }
|
| 224 |
|
| 225 |
|
| 226 | /* Put the filter into a paused state */
|
| 227 |
|
| 228 | STDMETHODIMP
|
| 229 | CBaseMediaFilter::Pause()
|
| 230 | {
|
| 231 | CAutoLock cObjectLock(m_pLock);
|
| 232 |
|
| 233 | m_State = State_Paused;
|
| 234 | return S_OK;
|
| 235 | }
|
| 236 |
|
| 237 |
|
| 238 | // Put the filter into a running state.
|
| 239 |
|
| 240 | // The time parameter is the offset to be added to the samples'
|
| 241 | // stream time to get the reference time at which they should be presented.
|
| 242 | //
|
| 243 | // you can either add these two and compare it against the reference clock,
|
| 244 | // or you can call CBaseMediaFilter::StreamTime and compare that against
|
| 245 | // the sample timestamp.
|
| 246 |
|
| 247 | STDMETHODIMP
|
| 248 | CBaseMediaFilter::Run(REFERENCE_TIME tStart)
|
| 249 | {
|
| 250 | CAutoLock cObjectLock(m_pLock);
|
| 251 |
|
| 252 | // remember the stream time offset
|
| 253 | m_tStart = tStart;
|
| 254 |
|
| 255 | if (m_State == State_Stopped){
|
| 256 | HRESULT hr = Pause();
|
| 257 |
|
| 258 | if (FAILED(hr)) {
|
| 259 | return hr;
|
| 260 | }
|
| 261 | }
|
| 262 | m_State = State_Running;
|
| 263 | return S_OK;
|
| 264 | }
|
| 265 |
|
| 266 |
|
| 267 | //
|
| 268 | // return the current stream time - samples with start timestamps of this
|
| 269 | // time or before should be rendered by now
|
| 270 | HRESULT
|
| 271 | CBaseMediaFilter::StreamTime(CRefTime& rtStream)
|
| 272 | {
|
| 273 | // Caller must lock for synchronization
|
| 274 | // We can't grab the filter lock because we want to be able to call
|
| 275 | // this from worker threads without deadlocking
|
| 276 |
|
| 277 | if (m_pClock == NULL) {
|
| 278 | return VFW_E_NO_CLOCK;
|
| 279 | }
|
| 280 |
|
| 281 | // get the current reference time
|
| 282 | HRESULT hr = m_pClock->GetTime((REFERENCE_TIME*)&rtStream);
|
| 283 | if (FAILED(hr)) {
|
| 284 | return hr;
|
| 285 | }
|
| 286 |
|
| 287 | // subtract the stream offset to get stream time
|
| 288 | rtStream -= m_tStart;
|
| 289 |
|
| 290 | return S_OK;
|
| 291 | }
|
| 292 |
|
| 293 |
|
| 294 | //=====================================================================
|
| 295 | //=====================================================================
|
| 296 | // Implements CBaseFilter
|
| 297 | //=====================================================================
|
| 298 | //=====================================================================
|
| 299 |
|
| 300 |
|
| 301 | /* Override this to say what interfaces we support and where */
|
| 302 |
|
| 303 | STDMETHODIMP CBaseFilter::NonDelegatingQueryInterface(REFIID riid,
|
| 304 | __deref_out void **ppv)
|
| 305 | {
|
| 306 | /* Do we have this interface */
|
| 307 |
|
| 308 | if (riid == IID_IBaseFilter) {
|
| 309 | return GetInterface((IBaseFilter *) this, ppv);
|
| 310 | } else if (riid == IID_IMediaFilter) {
|
| 311 | return GetInterface((IMediaFilter *) this, ppv);
|
| 312 | } else if (riid == IID_IPersist) {
|
| 313 | return GetInterface((IPersist *) this, ppv);
|
| 314 | } else if (riid == IID_IAMovieSetup) {
|
| 315 | return GetInterface((IAMovieSetup *) this, ppv);
|
| 316 | } else {
|
| 317 | return CUnknown::NonDelegatingQueryInterface(riid, ppv);
|
| 318 | }
|
| 319 | }
|
| 320 |
|
| 321 | #ifdef DEBUG
|
| 322 | STDMETHODIMP_(ULONG) CBaseFilter::NonDelegatingRelease()
|
| 323 | {
|
| 324 | if (m_cRef == 1) {
|
| 325 | KASSERT(m_pGraph == NULL);
|
| 326 | }
|
| 327 | return CUnknown::NonDelegatingRelease();
|
| 328 | }
|
| 329 | #endif
|
| 330 |
|
| 331 |
|
| 332 | /* Constructor */
|
| 333 |
|
| 334 | CBaseFilter::CBaseFilter(__in_opt LPCTSTR pName,
|
| 335 | __inout_opt LPUNKNOWN pUnk,
|
| 336 | __in CCritSec *pLock,
|
| 337 | REFCLSID clsid) :
|
| 338 | CUnknown( pName, pUnk ),
|
| 339 | m_pLock(pLock),
|
| 340 | m_clsid(clsid),
|
| 341 | m_State(State_Stopped),
|
| 342 | m_pClock(NULL),
|
| 343 | m_pGraph(NULL),
|
| 344 | m_pSink(NULL),
|
| 345 | m_pName(NULL),
|
| 346 | m_PinVersion(1)
|
| 347 | {
|
| 348 | #ifdef DXMPERF
|
| 349 | PERFLOG_CTOR( pName ? pName : L"CBaseFilter", (IBaseFilter *) this );
|
| 350 | #endif // DXMPERF
|
| 351 |
|
| 352 | ASSERT(pLock != NULL);
|
| 353 | }
|
| 354 |
|
| 355 | /* Passes in a redundant HRESULT argument */
|
| 356 |
|
| 357 | CBaseFilter::CBaseFilter(__in_opt LPCTSTR pName,
|
| 358 | __in_opt LPUNKNOWN pUnk,
|
| 359 | __in CCritSec *pLock,
|
| 360 | REFCLSID clsid,
|
| 361 | __inout HRESULT *phr) :
|
| 362 | CUnknown( pName, pUnk ),
|
| 363 | m_pLock(pLock),
|
| 364 | m_clsid(clsid),
|
| 365 | m_State(State_Stopped),
|
| 366 | m_pClock(NULL),
|
| 367 | m_pGraph(NULL),
|
| 368 | m_pSink(NULL),
|
| 369 | m_pName(NULL),
|
| 370 | m_PinVersion(1)
|
| 371 | {
|
| 372 | #ifdef DXMPERF
|
| 373 | PERFLOG_CTOR( pName ? pName : L"CBaseFilter", (IBaseFilter *) this );
|
| 374 | #endif // DXMPERF
|
| 375 |
|
| 376 | ASSERT(pLock != NULL);
|
| 377 | UNREFERENCED_PARAMETER(phr);
|
| 378 | }
|
| 379 |
|
| 380 | #ifdef UNICODE
|
| 381 | CBaseFilter::CBaseFilter(__in_opt LPCSTR pName,
|
| 382 | __in_opt LPUNKNOWN pUnk,
|
| 383 | __in CCritSec *pLock,
|
| 384 | REFCLSID clsid) :
|
| 385 | CUnknown( pName, pUnk ),
|
| 386 | m_pLock(pLock),
|
| 387 | m_clsid(clsid),
|
| 388 | m_State(State_Stopped),
|
| 389 | m_pClock(NULL),
|
| 390 | m_pGraph(NULL),
|
| 391 | m_pSink(NULL),
|
| 392 | m_pName(NULL),
|
| 393 | m_PinVersion(1)
|
| 394 | {
|
| 395 | #ifdef DXMPERF
|
| 396 | PERFLOG_CTOR( L"CBaseFilter", (IBaseFilter *) this );
|
| 397 | #endif // DXMPERF
|
| 398 |
|
| 399 | ASSERT(pLock != NULL);
|
| 400 | }
|
| 401 | CBaseFilter::CBaseFilter(__in_opt LPCSTR pName,
|
| 402 | __in_opt LPUNKNOWN pUnk,
|
| 403 | __in CCritSec *pLock,
|
| 404 | REFCLSID clsid,
|
| 405 | __inout HRESULT *phr) :
|
| 406 | CUnknown( pName, pUnk ),
|
| 407 | m_pLock(pLock),
|
| 408 | m_clsid(clsid),
|
| 409 | m_State(State_Stopped),
|
| 410 | m_pClock(NULL),
|
| 411 | m_pGraph(NULL),
|
| 412 | m_pSink(NULL),
|
| 413 | m_pName(NULL),
|
| 414 | m_PinVersion(1)
|
| 415 | {
|
| 416 | #ifdef DXMPERF
|
| 417 | PERFLOG_CTOR( L"CBaseFilter", (IBaseFilter *) this );
|
| 418 | #endif // DXMPERF
|
| 419 |
|
| 420 | ASSERT(pLock != NULL);
|
| 421 | UNREFERENCED_PARAMETER(phr);
|
| 422 | }
|
| 423 | #endif
|
| 424 |
|
| 425 | /* Destructor */
|
| 426 |
|
| 427 | CBaseFilter::~CBaseFilter()
|
| 428 | {
|
| 429 | #ifdef DXMPERF
|
| 430 | PERFLOG_DTOR( L"CBaseFilter", (IBaseFilter *) this );
|
| 431 | #endif // DXMPERF
|
| 432 |
|
| 433 | // NOTE we do NOT hold references on the filtergraph for m_pGraph or m_pSink
|
| 434 | // When we did we had the circular reference problem. Nothing would go away.
|
| 435 |
|
| 436 | delete[] m_pName;
|
| 437 |
|
| 438 | // must be stopped, but can't call Stop here since
|
| 439 | // our critsec has been destroyed.
|
| 440 |
|
| 441 | /* Release any clock we were using */
|
| 442 | if (m_pClock) {
|
| 443 | m_pClock->Release();
|
| 444 | m_pClock = NULL;
|
| 445 | }
|
| 446 | }
|
| 447 |
|
| 448 | /* Return the filter's clsid */
|
| 449 | STDMETHODIMP
|
| 450 | CBaseFilter::GetClassID(__out CLSID *pClsID)
|
| 451 | {
|
| 452 | CheckPointer(pClsID,E_POINTER);
|
| 453 | ValidateReadWritePtr(pClsID,sizeof(CLSID));
|
| 454 | *pClsID = m_clsid;
|
| 455 | return NOERROR;
|
| 456 | }
|
| 457 |
|
| 458 | /* Override this if your state changes are not done synchronously */
|
| 459 | STDMETHODIMP
|
| 460 | CBaseFilter::GetState(DWORD dwMSecs, __out FILTER_STATE *State)
|
| 461 | {
|
| 462 | UNREFERENCED_PARAMETER(dwMSecs);
|
| 463 | CheckPointer(State,E_POINTER);
|
| 464 | ValidateReadWritePtr(State,sizeof(FILTER_STATE));
|
| 465 |
|
| 466 | *State = m_State;
|
| 467 | return S_OK;
|
| 468 | }
|
| 469 |
|
| 470 |
|
| 471 | /* Set the clock we will use for synchronisation */
|
| 472 |
|
| 473 | STDMETHODIMP
|
| 474 | CBaseFilter::SetSyncSource(__in_opt IReferenceClock *pClock)
|
| 475 | {
|
| 476 | CAutoLock cObjectLock(m_pLock);
|
| 477 |
|
| 478 | // Ensure the new one does not go away - even if the same as the old
|
| 479 | if (pClock) {
|
| 480 | pClock->AddRef();
|
| 481 | }
|
| 482 |
|
| 483 | // if we have a clock, release it
|
| 484 | if (m_pClock) {
|
| 485 | m_pClock->Release();
|
| 486 | }
|
| 487 |
|
| 488 | // Set the new reference clock (might be NULL)
|
| 489 | // Should we query it to ensure it is a clock? Consider for a debug build.
|
| 490 | m_pClock = pClock;
|
| 491 |
|
| 492 | return NOERROR;
|
| 493 | }
|
| 494 |
|
| 495 | /* Return the clock we are using for synchronisation */
|
| 496 | STDMETHODIMP
|
| 497 | CBaseFilter::GetSyncSource(__deref_out_opt IReferenceClock **pClock)
|
| 498 | {
|
| 499 | CheckPointer(pClock,E_POINTER);
|
| 500 | ValidateReadWritePtr(pClock,sizeof(IReferenceClock *));
|
| 501 | CAutoLock cObjectLock(m_pLock);
|
| 502 |
|
| 503 | if (m_pClock) {
|
| 504 | // returning an interface... addref it...
|
| 505 | m_pClock->AddRef();
|
| 506 | }
|
| 507 | *pClock = (IReferenceClock*)m_pClock;
|
| 508 | return NOERROR;
|
| 509 | }
|
| 510 |
|
| 511 |
|
| 512 |
|
| 513 | // override CBaseMediaFilter Stop method, to deactivate any pins this
|
| 514 | // filter has.
|
| 515 | STDMETHODIMP
|
| 516 | CBaseFilter::Stop()
|
| 517 | {
|
| 518 | CAutoLock cObjectLock(m_pLock);
|
| 519 | HRESULT hr = NOERROR;
|
| 520 |
|
| 521 | // notify all pins of the state change
|
| 522 | if (m_State != State_Stopped) {
|
| 523 | int cPins = GetPinCount();
|
| 524 | for (int c = 0; c < cPins; c++) {
|
| 525 |
|
| 526 | CBasePin *pPin = GetPin(c);
|
| 527 | if (NULL == pPin) {
|
| 528 | break;
|
| 529 | }
|
| 530 |
|
| 531 | // Disconnected pins are not activated - this saves pins worrying
|
| 532 | // about this state themselves. We ignore the return code to make
|
| 533 | // sure everyone is inactivated regardless. The base input pin
|
| 534 | // class can return an error if it has no allocator but Stop can
|
| 535 | // be used to resync the graph state after something has gone bad
|
| 536 |
|
| 537 | if (pPin->IsConnected()) {
|
| 538 | HRESULT hrTmp = pPin->Inactive();
|
| 539 | if (FAILED(hrTmp) && SUCCEEDED(hr)) {
|
| 540 | hr = hrTmp;
|
| 541 | }
|
| 542 | }
|
| 543 | }
|
| 544 | }
|
| 545 |
|
| 546 | #ifdef DXMPERF
|
| 547 | PERFLOG_STOP( m_pName ? m_pName : L"CBaseFilter", (IBaseFilter *) this, m_State );
|
| 548 | #endif // DXMPERF
|
| 549 |
|
| 550 | m_State = State_Stopped;
|
| 551 | return hr;
|
| 552 | }
|
| 553 |
|
| 554 |
|
| 555 | // override CBaseMediaFilter Pause method to activate any pins
|
| 556 | // this filter has (also called from Run)
|
| 557 |
|
| 558 | STDMETHODIMP
|
| 559 | CBaseFilter::Pause()
|
| 560 | {
|
| 561 | CAutoLock cObjectLock(m_pLock);
|
| 562 |
|
| 563 | // notify all pins of the change to active state
|
| 564 | if (m_State == State_Stopped) {
|
| 565 | int cPins = GetPinCount();
|
| 566 | for (int c = 0; c < cPins; c++) {
|
| 567 |
|
| 568 | CBasePin *pPin = GetPin(c);
|
| 569 | if (NULL == pPin) {
|
| 570 | break;
|
| 571 | }
|
| 572 |
|
| 573 | // Disconnected pins are not activated - this saves pins
|
| 574 | // worrying about this state themselves
|
| 575 |
|
| 576 | if (pPin->IsConnected()) {
|
| 577 | HRESULT hr = pPin->Active();
|
| 578 | if (FAILED(hr)) {
|
| 579 | return hr;
|
| 580 | }
|
| 581 | }
|
| 582 | }
|
| 583 | }
|
| 584 |
|
| 585 |
|
| 586 | #ifdef DXMPERF
|
| 587 | PERFLOG_PAUSE( m_pName ? m_pName : L"CBaseFilter", (IBaseFilter *) this, m_State );
|
| 588 | #endif // DXMPERF
|
| 589 |
|
| 590 | m_State = State_Paused;
|
| 591 | return S_OK;
|
| 592 | }
|
| 593 |
|
| 594 | // Put the filter into a running state.
|
| 595 |
|
| 596 | // The time parameter is the offset to be added to the samples'
|
| 597 | // stream time to get the reference time at which they should be presented.
|
| 598 | //
|
| 599 | // you can either add these two and compare it against the reference clock,
|
| 600 | // or you can call CBaseFilter::StreamTime and compare that against
|
| 601 | // the sample timestamp.
|
| 602 |
|
| 603 | STDMETHODIMP
|
| 604 | CBaseFilter::Run(REFERENCE_TIME tStart)
|
| 605 | {
|
| 606 | CAutoLock cObjectLock(m_pLock);
|
| 607 |
|
| 608 | // remember the stream time offset
|
| 609 | m_tStart = tStart;
|
| 610 |
|
| 611 | if (m_State == State_Stopped){
|
| 612 | HRESULT hr = Pause();
|
| 613 |
|
| 614 | if (FAILED(hr)) {
|
| 615 | return hr;
|
| 616 | }
|
| 617 | }
|
| 618 | // notify all pins of the change to active state
|
| 619 | if (m_State != State_Running) {
|
| 620 | int cPins = GetPinCount();
|
| 621 | for (int c = 0; c < cPins; c++) {
|
| 622 |
|
| 623 | CBasePin *pPin = GetPin(c);
|
| 624 | if (NULL == pPin) {
|
| 625 | break;
|
| 626 | }
|
| 627 |
|
| 628 | // Disconnected pins are not activated - this saves pins
|
| 629 | // worrying about this state themselves
|
| 630 |
|
| 631 | if (pPin->IsConnected()) {
|
| 632 | HRESULT hr = pPin->Run(tStart);
|
| 633 | if (FAILED(hr)) {
|
| 634 | return hr;
|
| 635 | }
|
| 636 | }
|
| 637 | }
|
| 638 | }
|
| 639 |
|
| 640 | #ifdef DXMPERF
|
| 641 | PERFLOG_RUN( m_pName ? m_pName : L"CBaseFilter", (IBaseFilter *) this, tStart, m_State );
|
| 642 | #endif // DXMPERF
|
| 643 |
|
| 644 | m_State = State_Running;
|
| 645 | return S_OK;
|
| 646 | }
|
| 647 |
|
| 648 | //
|
| 649 | // return the current stream time - samples with start timestamps of this
|
| 650 | // time or before should be rendered by now
|
| 651 | HRESULT
|
| 652 | CBaseFilter::StreamTime(CRefTime& rtStream)
|
| 653 | {
|
| 654 | // Caller must lock for synchronization
|
| 655 | // We can't grab the filter lock because we want to be able to call
|
| 656 | // this from worker threads without deadlocking
|
| 657 |
|
| 658 | if (m_pClock == NULL) {
|
| 659 | return VFW_E_NO_CLOCK;
|
| 660 | }
|
| 661 |
|
| 662 | // get the current reference time
|
| 663 | HRESULT hr = m_pClock->GetTime((REFERENCE_TIME*)&rtStream);
|
| 664 | if (FAILED(hr)) {
|
| 665 | return hr;
|
| 666 | }
|
| 667 |
|
| 668 | // subtract the stream offset to get stream time
|
| 669 | rtStream -= m_tStart;
|
| 670 |
|
| 671 | return S_OK;
|
| 672 | }
|
| 673 |
|
| 674 |
|
| 675 | /* Create an enumerator for the pins attached to this filter */
|
| 676 |
|
| 677 | STDMETHODIMP
|
| 678 | CBaseFilter::EnumPins(__deref_out IEnumPins **ppEnum)
|
| 679 | {
|
| 680 | CheckPointer(ppEnum,E_POINTER);
|
| 681 | ValidateReadWritePtr(ppEnum,sizeof(IEnumPins *));
|
| 682 |
|
| 683 | /* Create a new ref counted enumerator */
|
| 684 |
|
| 685 | *ppEnum = new CEnumPins(this,
|
| 686 | NULL);
|
| 687 |
|
| 688 | return *ppEnum == NULL ? E_OUTOFMEMORY : NOERROR;
|
| 689 | }
|
| 690 |
|
| 691 |
|
| 692 | // default behaviour of FindPin is to assume pins are named
|
| 693 | // by their pin names
|
| 694 | STDMETHODIMP
|
| 695 | CBaseFilter::FindPin(
|
| 696 | LPCWSTR Id,
|
| 697 | __deref_out IPin ** ppPin
|
| 698 | )
|
| 699 | {
|
| 700 | CheckPointer(ppPin,E_POINTER);
|
| 701 | ValidateReadWritePtr(ppPin,sizeof(IPin *));
|
| 702 |
|
| 703 | // We're going to search the pin list so maintain integrity
|
| 704 | CAutoLock lck(m_pLock);
|
| 705 | int iCount = GetPinCount();
|
| 706 | for (int i = 0; i < iCount; i++) {
|
| 707 | CBasePin *pPin = GetPin(i);
|
| 708 | if (NULL == pPin) {
|
| 709 | break;
|
| 710 | }
|
| 711 |
|
| 712 | if (0 == lstrcmpW(pPin->Name(), Id)) {
|
| 713 | // Found one that matches
|
| 714 | //
|
| 715 | // AddRef() and return it
|
| 716 | *ppPin = pPin;
|
| 717 | pPin->AddRef();
|
| 718 | return S_OK;
|
| 719 | }
|
| 720 | }
|
| 721 | *ppPin = NULL;
|
| 722 | return VFW_E_NOT_FOUND;
|
| 723 | }
|
| 724 |
|
| 725 | /* Return information about this filter */
|
| 726 |
|
| 727 | STDMETHODIMP
|
| 728 | CBaseFilter::QueryFilterInfo(__out FILTER_INFO * pInfo)
|
| 729 | {
|
| 730 | CheckPointer(pInfo,E_POINTER);
|
| 731 | ValidateReadWritePtr(pInfo,sizeof(FILTER_INFO));
|
| 732 |
|
| 733 | if (m_pName) {
|
| 734 | (void)StringCchCopyW(pInfo->achName, NUMELMS(pInfo->achName), m_pName);
|
| 735 | } else {
|
| 736 | pInfo->achName[0] = L'\0';
|
| 737 | }
|
| 738 | pInfo->pGraph = m_pGraph;
|
| 739 | if (m_pGraph)
|
| 740 | m_pGraph->AddRef();
|
| 741 | return NOERROR;
|
| 742 | }
|
| 743 |
|
| 744 |
|
| 745 | /* Provide the filter with a filter graph */
|
| 746 |
|
| 747 | STDMETHODIMP
|
| 748 | CBaseFilter::JoinFilterGraph(
|
| 749 | __inout_opt IFilterGraph * pGraph,
|
| 750 | __in_opt LPCWSTR pName)
|
| 751 | {
|
| 752 | CAutoLock cObjectLock(m_pLock);
|
| 753 |
|
| 754 | // NOTE: we no longer hold references on the graph (m_pGraph, m_pSink)
|
| 755 |
|
| 756 | m_pGraph = pGraph;
|
| 757 | if (m_pGraph) {
|
| 758 | HRESULT hr = m_pGraph->QueryInterface(IID_IMediaEventSink,
|
| 759 | (void**) &m_pSink);
|
| 760 | if (FAILED(hr)) {
|
| 761 | ASSERT(m_pSink == NULL);
|
| 762 | }
|
| 763 | else m_pSink->Release(); // we do NOT keep a reference on it.
|
| 764 | } else {
|
| 765 | // if graph pointer is null, then we should
|
| 766 | // also release the IMediaEventSink on the same object - we don't
|
| 767 | // refcount it, so just set it to null
|
| 768 | m_pSink = NULL;
|
| 769 | }
|
| 770 |
|
| 771 |
|
| 772 | if (m_pName) {
|
| 773 | delete[] m_pName;
|
| 774 | m_pName = NULL;
|
| 775 | }
|
| 776 |
|
| 777 | if (pName) {
|
| 778 | size_t namelen;
|
| 779 | HRESULT hr = StringCchLengthW(pName, STRSAFE_MAX_CCH, &namelen);
|
| 780 | if (FAILED(hr)) {
|
| 781 | return hr;
|
| 782 | }
|
| 783 | m_pName = new WCHAR[namelen + 1];
|
| 784 | if (m_pName) {
|
| 785 | (void)StringCchCopyW(m_pName, namelen + 1, pName);
|
| 786 | } else {
|
| 787 | return E_OUTOFMEMORY;
|
| 788 | }
|
| 789 | }
|
| 790 |
|
| 791 | #ifdef DXMPERF
|
| 792 | PERFLOG_JOINGRAPH( m_pName ? m_pName : L"CBaseFilter",(IBaseFilter *) this, pGraph );
|
| 793 | #endif // DXMPERF
|
| 794 |
|
| 795 | return NOERROR;
|
| 796 | }
|
| 797 |
|
| 798 |
|
| 799 | // return a Vendor information string. Optional - may return E_NOTIMPL.
|
| 800 | // memory returned should be freed using CoTaskMemFree
|
| 801 | // default implementation returns E_NOTIMPL
|
| 802 | STDMETHODIMP
|
| 803 | CBaseFilter::QueryVendorInfo(
|
| 804 | __deref_out LPWSTR* pVendorInfo)
|
| 805 | {
|
| 806 | UNREFERENCED_PARAMETER(pVendorInfo);
|
| 807 | return E_NOTIMPL;
|
| 808 | }
|
| 809 |
|
| 810 |
|
| 811 | // send an event notification to the filter graph if we know about it.
|
| 812 | // returns S_OK if delivered, S_FALSE if the filter graph does not sink
|
| 813 | // events, or an error otherwise.
|
| 814 | HRESULT
|
| 815 | CBaseFilter::NotifyEvent(
|
| 816 | long EventCode,
|
| 817 | LONG_PTR EventParam1,
|
| 818 | LONG_PTR EventParam2)
|
| 819 | {
|
| 820 | // Snapshot so we don't have to lock up
|
| 821 | IMediaEventSink *pSink = m_pSink;
|
| 822 | if (pSink) {
|
| 823 | if (EC_COMPLETE == EventCode) {
|
| 824 | EventParam2 = (LONG_PTR)(IBaseFilter*)this;
|
| 825 | }
|
| 826 |
|
| 827 | return pSink->Notify(EventCode, EventParam1, EventParam2);
|
| 828 | } else {
|
| 829 | return E_NOTIMPL;
|
| 830 | }
|
| 831 | }
|
| 832 |
|
| 833 | // Request reconnect
|
| 834 | // pPin is the pin to reconnect
|
| 835 | // pmt is the type to reconnect with - can be NULL
|
| 836 | // Calls ReconnectEx on the filter graph
|
| 837 | HRESULT
|
| 838 | CBaseFilter::ReconnectPin(
|
| 839 | IPin *pPin,
|
| 840 | __in_opt AM_MEDIA_TYPE const *pmt
|
| 841 | )
|
| 842 | {
|
| 843 | IFilterGraph2 *pGraph2;
|
| 844 | if (m_pGraph != NULL) {
|
| 845 | HRESULT hr = m_pGraph->QueryInterface(IID_IFilterGraph2, (void **)&pGraph2);
|
| 846 | if (SUCCEEDED(hr)) {
|
| 847 | hr = pGraph2->ReconnectEx(pPin, pmt);
|
| 848 | pGraph2->Release();
|
| 849 | return hr;
|
| 850 | } else {
|
| 851 | return m_pGraph->Reconnect(pPin);
|
| 852 | }
|
| 853 | } else {
|
| 854 | return E_NOINTERFACE;
|
| 855 | }
|
| 856 | }
|
| 857 |
|
| 858 |
|
| 859 |
|
| 860 | /* This is the same idea as the media type version does for type enumeration
|
| 861 | on pins but for the list of pins available. So if the list of pins you
|
| 862 | provide changes dynamically then either override this virtual function
|
| 863 | to provide the version number, or more simply call IncrementPinVersion */
|
| 864 |
|
| 865 | LONG CBaseFilter::GetPinVersion()
|
| 866 | {
|
| 867 | return m_PinVersion;
|
| 868 | }
|
| 869 |
|
| 870 |
|
| 871 | /* Increment the current pin version cookie */
|
| 872 |
|
| 873 | void CBaseFilter::IncrementPinVersion()
|
| 874 | {
|
| 875 | InterlockedIncrement(&m_PinVersion);
|
| 876 | }
|
| 877 |
|
| 878 | /* register filter */
|
| 879 |
|
| 880 | STDMETHODIMP CBaseFilter::Register()
|
| 881 | {
|
| 882 | // get setup data, if it exists
|
| 883 | //
|
| 884 | LPAMOVIESETUP_FILTER psetupdata = GetSetupData();
|
| 885 |
|
| 886 | // check we've got data
|
| 887 | //
|
| 888 | if( NULL == psetupdata ) return S_FALSE;
|
| 889 |
|
| 890 | // init is ref counted so call just in case
|
| 891 | // we're being called cold.
|
| 892 | //
|
| 893 | HRESULT hr = CoInitialize( (LPVOID)NULL );
|
| 894 | ASSERT( SUCCEEDED(hr) );
|
| 895 |
|
| 896 | // get hold of IFilterMapper
|
| 897 | //
|
| 898 | IFilterMapper *pIFM;
|
| 899 | hr = CoCreateInstance( CLSID_FilterMapper
|
| 900 | , NULL
|
| 901 | , CLSCTX_INPROC_SERVER
|
| 902 | , IID_IFilterMapper
|
| 903 | , (void **)&pIFM );
|
| 904 | if( SUCCEEDED(hr) )
|
| 905 | {
|
| 906 | hr = AMovieSetupRegisterFilter( psetupdata, pIFM, TRUE );
|
| 907 | pIFM->Release();
|
| 908 | }
|
| 909 |
|
| 910 | // and clear up
|
| 911 | //
|
| 912 | CoFreeUnusedLibraries();
|
| 913 | CoUninitialize();
|
| 914 |
|
| 915 | return NOERROR;
|
| 916 | }
|
| 917 |
|
| 918 |
|
| 919 | /* unregister filter */
|
| 920 |
|
| 921 | STDMETHODIMP CBaseFilter::Unregister()
|
| 922 | {
|
| 923 | // get setup data, if it exists
|
| 924 | //
|
| 925 | LPAMOVIESETUP_FILTER psetupdata = GetSetupData();
|
| 926 |
|
| 927 | // check we've got data
|
| 928 | //
|
| 929 | if( NULL == psetupdata ) return S_FALSE;
|
| 930 |
|
| 931 | // OLE init is ref counted so call
|
| 932 | // just in case we're being called cold.
|
| 933 | //
|
| 934 | HRESULT hr = CoInitialize( (LPVOID)NULL );
|
| 935 | ASSERT( SUCCEEDED(hr) );
|
| 936 |
|
| 937 | // get hold of IFilterMapper
|
| 938 | //
|
| 939 | IFilterMapper *pIFM;
|
| 940 | hr = CoCreateInstance( CLSID_FilterMapper
|
| 941 | , NULL
|
| 942 | , CLSCTX_INPROC_SERVER
|
| 943 | , IID_IFilterMapper
|
| 944 | , (void **)&pIFM );
|
| 945 | if( SUCCEEDED(hr) )
|
| 946 | {
|
| 947 | hr = AMovieSetupRegisterFilter( psetupdata, pIFM, FALSE );
|
| 948 |
|
| 949 | // release interface
|
| 950 | //
|
| 951 | pIFM->Release();
|
| 952 | }
|
| 953 |
|
| 954 | // clear up
|
| 955 | //
|
| 956 | CoFreeUnusedLibraries();
|
| 957 | CoUninitialize();
|
| 958 |
|
| 959 | // handle one acceptable "error" - that
|
| 960 | // of filter not being registered!
|
| 961 | // (couldn't find a suitable #define'd
|
| 962 | // name for the error!)
|
| 963 | //
|
| 964 | if( 0x80070002 == hr)
|
| 965 | return NOERROR;
|
| 966 | else
|
| 967 | return hr;
|
| 968 | }
|
| 969 |
|
| 970 |
|
| 971 | //=====================================================================
|
| 972 | //=====================================================================
|
| 973 | // Implements CEnumPins
|
| 974 | //=====================================================================
|
| 975 | //=====================================================================
|
| 976 |
|
| 977 |
|
| 978 | CEnumPins::CEnumPins(__in CBaseFilter *pFilter,
|
| 979 | __in_opt CEnumPins *pEnumPins) :
|
| 980 | m_Position(0),
|
| 981 | m_PinCount(0),
|
| 982 | m_pFilter(pFilter),
|
| 983 | m_cRef(1), // Already ref counted
|
| 984 | m_PinCache(NAME("Pin Cache"))
|
| 985 | {
|
| 986 |
|
| 987 | #ifdef DEBUG
|
| 988 | m_dwCookie = DbgRegisterObjectCreation("CEnumPins", 0);
|
| 989 | #endif
|
| 990 |
|
| 991 | /* We must be owned by a filter derived from CBaseFilter */
|
| 992 |
|
| 993 | ASSERT(pFilter != NULL);
|
| 994 |
|
| 995 | /* Hold a reference count on our filter */
|
| 996 | m_pFilter->AddRef();
|
| 997 |
|
| 998 | /* Are we creating a new enumerator */
|
| 999 |
|
| 1000 | if (pEnumPins == NULL) {
|
| 1001 | m_Version = m_pFilter->GetPinVersion();
|
| 1002 | m_PinCount = m_pFilter->GetPinCount();
|
| 1003 | } else {
|
| 1004 | ASSERT(m_Position <= m_PinCount);
|
| 1005 | m_Position = pEnumPins->m_Position;
|
| 1006 | m_PinCount = pEnumPins->m_PinCount;
|
| 1007 | m_Version = pEnumPins->m_Version;
|
| 1008 | m_PinCache.AddTail(&(pEnumPins->m_PinCache));
|
| 1009 | }
|
| 1010 | }
|
| 1011 |
|
| 1012 |
|
| 1013 | /* Destructor releases the reference count on our filter NOTE since we hold
|
| 1014 | a reference count on the filter who created us we know it is safe to
|
| 1015 | release it, no access can be made to it afterwards though as we have just
|
| 1016 | caused the last reference count to go and the object to be deleted */
|
| 1017 |
|
| 1018 | CEnumPins::~CEnumPins()
|
| 1019 | {
|
| 1020 | m_pFilter->Release();
|
| 1021 |
|
| 1022 | #ifdef DEBUG
|
| 1023 | DbgRegisterObjectDestruction(m_dwCookie);
|
| 1024 | #endif
|
| 1025 | }
|
| 1026 |
|
| 1027 |
|
| 1028 | /* Override this to say what interfaces we support where */
|
| 1029 |
|
| 1030 | STDMETHODIMP
|
| 1031 | CEnumPins::QueryInterface(REFIID riid, __deref_out void **ppv)
|
| 1032 | {
|
| 1033 | CheckPointer(ppv, E_POINTER);
|
| 1034 |
|
| 1035 | /* Do we have this interface */
|
| 1036 |
|
| 1037 | if (riid == IID_IEnumPins || riid == IID_IUnknown) {
|
| 1038 | return GetInterface((IEnumPins *) this, ppv);
|
| 1039 | } else {
|
| 1040 | *ppv = NULL;
|
| 1041 | return E_NOINTERFACE;
|
| 1042 | }
|
| 1043 | }
|
| 1044 |
|
| 1045 | STDMETHODIMP_(ULONG)
|
| 1046 | CEnumPins::AddRef()
|
| 1047 | {
|
| 1048 | return InterlockedIncrement(&m_cRef);
|
| 1049 | }
|
| 1050 |
|
| 1051 | STDMETHODIMP_(ULONG)
|
| 1052 | CEnumPins::Release()
|
| 1053 | {
|
| 1054 | ULONG cRef = InterlockedDecrement(&m_cRef);
|
| 1055 | if (cRef == 0) {
|
| 1056 | delete this;
|
| 1057 | }
|
| 1058 | return cRef;
|
| 1059 | }
|
| 1060 |
|
| 1061 | /* One of an enumerator's basic member functions allows us to create a cloned
|
| 1062 | interface that initially has the same state. Since we are taking a snapshot
|
| 1063 | of an object (current position and all) we must lock access at the start */
|
| 1064 |
|
| 1065 | STDMETHODIMP
|
| 1066 | CEnumPins::Clone(__deref_out IEnumPins **ppEnum)
|
| 1067 | {
|
| 1068 | CheckPointer(ppEnum,E_POINTER);
|
| 1069 | ValidateReadWritePtr(ppEnum,sizeof(IEnumPins *));
|
| 1070 | HRESULT hr = NOERROR;
|
| 1071 |
|
| 1072 | /* Check we are still in sync with the filter */
|
| 1073 | if (AreWeOutOfSync() == TRUE) {
|
| 1074 | *ppEnum = NULL;
|
| 1075 | hr = VFW_E_ENUM_OUT_OF_SYNC;
|
| 1076 | } else {
|
| 1077 | *ppEnum = new CEnumPins(m_pFilter,
|
| 1078 | this);
|
| 1079 | if (*ppEnum == NULL) {
|
| 1080 | hr = E_OUTOFMEMORY;
|
| 1081 | }
|
| 1082 | }
|
| 1083 | return hr;
|
| 1084 | }
|
| 1085 |
|
| 1086 |
|
| 1087 | /* Return the next pin after the current position */
|
| 1088 |
|
| 1089 | STDMETHODIMP
|
| 1090 | CEnumPins::Next(ULONG cPins, // place this many pins...
|
| 1091 | __out_ecount(cPins) IPin **ppPins, // ...in this array
|
| 1092 | __out_opt ULONG *pcFetched) // actual count passed returned here
|
| 1093 | {
|
| 1094 | CheckPointer(ppPins,E_POINTER);
|
| 1095 | ValidateReadWritePtr(ppPins,cPins * sizeof(IPin *));
|
| 1096 |
|
| 1097 | ASSERT(ppPins);
|
| 1098 |
|
| 1099 | if (pcFetched!=NULL) {
|
| 1100 | ValidateWritePtr(pcFetched, sizeof(ULONG));
|
| 1101 | *pcFetched = 0; // default unless we succeed
|
| 1102 | }
|
| 1103 | // now check that the parameter is valid
|
| 1104 | else if (cPins>1) { // pcFetched == NULL
|
| 1105 | return E_INVALIDARG;
|
| 1106 | }
|
| 1107 | ULONG cFetched = 0; // increment as we get each one.
|
| 1108 |
|
| 1109 | /* Check we are still in sync with the filter */
|
| 1110 | if (AreWeOutOfSync() == TRUE) {
|
| 1111 | // If we are out of sync, we should refresh the enumerator.
|
| 1112 | // This will reset the position and update the other members, but
|
| 1113 | // will not clear cache of pins we have already returned.
|
| 1114 | Refresh();
|
| 1115 | }
|
| 1116 |
|
| 1117 | /* Return each pin interface NOTE GetPin returns CBasePin * not addrefed
|
| 1118 | so we must QI for the IPin (which increments its reference count)
|
| 1119 | If while we are retrieving a pin from the filter an error occurs we
|
| 1120 | assume that our internal state is stale with respect to the filter
|
| 1121 | (for example someone has deleted a pin) so we
|
| 1122 | return VFW_E_ENUM_OUT_OF_SYNC */
|
| 1123 |
|
| 1124 | while (cFetched < cPins && m_PinCount > m_Position) {
|
| 1125 |
|
| 1126 | /* Get the next pin object from the filter */
|
| 1127 |
|
| 1128 | CBasePin *pPin = m_pFilter->GetPin(m_Position++);
|
| 1129 | if (pPin == NULL) {
|
| 1130 | // If this happend, and it's not the first time through, then we've got a problem,
|
| 1131 | // since we should really go back and release the iPins, which we have previously
|
| 1132 | // AddRef'ed.
|
| 1133 | ASSERT( cFetched==0 );
|
| 1134 | return VFW_E_ENUM_OUT_OF_SYNC;
|
| 1135 | }
|
| 1136 |
|
| 1137 | /* We only want to return this pin, if it is not in our cache */
|
| 1138 | if (0 == m_PinCache.Find(pPin))
|
| 1139 | {
|
| 1140 | /* From the object get an IPin interface */
|
| 1141 |
|
| 1142 | *ppPins = pPin;
|
| 1143 | pPin->AddRef();
|
| 1144 |
|
| 1145 | cFetched++;
|
| 1146 | ppPins++;
|
| 1147 |
|
| 1148 | m_PinCache.AddTail(pPin);
|
| 1149 | }
|
| 1150 | }
|
| 1151 |
|
| 1152 | if (pcFetched!=NULL) {
|
| 1153 | *pcFetched = cFetched;
|
| 1154 | }
|
| 1155 |
|
| 1156 | return (cPins==cFetched ? NOERROR : S_FALSE);
|
| 1157 | }
|
| 1158 |
|
| 1159 |
|
| 1160 | /* Skip over one or more entries in the enumerator */
|
| 1161 |
|
| 1162 | STDMETHODIMP
|
| 1163 | CEnumPins::Skip(ULONG cPins)
|
| 1164 | {
|
| 1165 | /* Check we are still in sync with the filter */
|
| 1166 | if (AreWeOutOfSync() == TRUE) {
|
| 1167 | return VFW_E_ENUM_OUT_OF_SYNC;
|
| 1168 | }
|
| 1169 |
|
| 1170 | /* Work out how many pins are left to skip over */
|
| 1171 | /* We could position at the end if we are asked to skip too many... */
|
| 1172 | /* ..which would match the base implementation for CEnumMediaTypes::Skip */
|
| 1173 |
|
| 1174 | ULONG PinsLeft = m_PinCount - m_Position;
|
| 1175 | if (cPins > PinsLeft) {
|
| 1176 | return S_FALSE;
|
| 1177 | }
|
| 1178 | m_Position += cPins;
|
| 1179 | return NOERROR;
|
| 1180 | }
|
| 1181 |
|
| 1182 |
|
| 1183 | /* Set the current position back to the start */
|
| 1184 | /* Reset has 4 simple steps:
|
| 1185 | *
|
| 1186 | * Set position to head of list
|
| 1187 | * Sync enumerator with object being enumerated
|
| 1188 | * Clear the cache of pins already returned
|
| 1189 | * return S_OK
|
| 1190 | */
|
| 1191 |
|
| 1192 | STDMETHODIMP
|
| 1193 | CEnumPins::Reset()
|
| 1194 | {
|
| 1195 | m_Version = m_pFilter->GetPinVersion();
|
| 1196 | m_PinCount = m_pFilter->GetPinCount();
|
| 1197 |
|
| 1198 | m_Position = 0;
|
| 1199 |
|
| 1200 | // Clear the cache
|
| 1201 | m_PinCache.RemoveAll();
|
| 1202 |
|
| 1203 | return S_OK;
|
| 1204 | }
|
| 1205 |
|
| 1206 |
|
| 1207 | /* Set the current position back to the start */
|
| 1208 | /* Refresh has 3 simple steps:
|
| 1209 | *
|
| 1210 | * Set position to head of list
|
| 1211 | * Sync enumerator with object being enumerated
|
| 1212 | * return S_OK
|
| 1213 | */
|
| 1214 |
|
| 1215 | STDMETHODIMP
|
| 1216 | CEnumPins::Refresh()
|
| 1217 | {
|
| 1218 | m_Version = m_pFilter->GetPinVersion();
|
| 1219 | m_PinCount = m_pFilter->GetPinCount();
|
| 1220 |
|
| 1221 | m_Position = 0;
|
| 1222 | return S_OK;
|
| 1223 | }
|
| 1224 |
|
| 1225 |
|
| 1226 | //=====================================================================
|
| 1227 | //=====================================================================
|
| 1228 | // Implements CEnumMediaTypes
|
| 1229 | //=====================================================================
|
| 1230 | //=====================================================================
|
| 1231 |
|
| 1232 |
|
| 1233 | CEnumMediaTypes::CEnumMediaTypes(__in CBasePin *pPin,
|
| 1234 | __in_opt CEnumMediaTypes *pEnumMediaTypes) :
|
| 1235 | m_Position(0),
|
| 1236 | m_pPin(pPin),
|
| 1237 | m_cRef(1)
|
| 1238 | {
|
| 1239 |
|
| 1240 | #ifdef DEBUG
|
| 1241 | m_dwCookie = DbgRegisterObjectCreation("CEnumMediaTypes", 0);
|
| 1242 | #endif
|
| 1243 |
|
| 1244 | /* We must be owned by a pin derived from CBasePin */
|
| 1245 |
|
| 1246 | ASSERT(pPin != NULL);
|
| 1247 |
|
| 1248 | /* Hold a reference count on our pin */
|
| 1249 | m_pPin->AddRef();
|
| 1250 |
|
| 1251 | /* Are we creating a new enumerator */
|
| 1252 |
|
| 1253 | if (pEnumMediaTypes == NULL) {
|
| 1254 | m_Version = m_pPin->GetMediaTypeVersion();
|
| 1255 | return;
|
| 1256 | }
|
| 1257 |
|
| 1258 | m_Position = pEnumMediaTypes->m_Position;
|
| 1259 | m_Version = pEnumMediaTypes->m_Version;
|
| 1260 | }
|
| 1261 |
|
| 1262 |
|
| 1263 | /* Destructor releases the reference count on our base pin. NOTE since we hold
|
| 1264 | a reference count on the pin who created us we know it is safe to release
|
| 1265 | it, no access can be made to it afterwards though as we might have just
|
| 1266 | caused the last reference count to go and the object to be deleted */
|
| 1267 |
|
| 1268 | CEnumMediaTypes::~CEnumMediaTypes()
|
| 1269 | {
|
| 1270 | #ifdef DEBUG
|
| 1271 | DbgRegisterObjectDestruction(m_dwCookie);
|
| 1272 | #endif
|
| 1273 | m_pPin->Release();
|
| 1274 | }
|
| 1275 |
|
| 1276 |
|
| 1277 | /* Override this to say what interfaces we support where */
|
| 1278 |
|
| 1279 | STDMETHODIMP
|
| 1280 | CEnumMediaTypes::QueryInterface(REFIID riid, __deref_out void **ppv)
|
| 1281 | {
|
| 1282 | CheckPointer(ppv, E_POINTER);
|
| 1283 |
|
| 1284 | /* Do we have this interface */
|
| 1285 |
|
| 1286 | if (riid == IID_IEnumMediaTypes || riid == IID_IUnknown) {
|
| 1287 | return GetInterface((IEnumMediaTypes *) this, ppv);
|
| 1288 | } else {
|
| 1289 | *ppv = NULL;
|
| 1290 | return E_NOINTERFACE;
|
| 1291 | }
|
| 1292 | }
|
| 1293 |
|
| 1294 | STDMETHODIMP_(ULONG)
|
| 1295 | CEnumMediaTypes::AddRef()
|
| 1296 | {
|
| 1297 | return InterlockedIncrement(&m_cRef);
|
| 1298 | }
|
| 1299 |
|
| 1300 | STDMETHODIMP_(ULONG)
|
| 1301 | CEnumMediaTypes::Release()
|
| 1302 | {
|
| 1303 | ULONG cRef = InterlockedDecrement(&m_cRef);
|
| 1304 | if (cRef == 0) {
|
| 1305 | delete this;
|
| 1306 | }
|
| 1307 | return cRef;
|
| 1308 | }
|
| 1309 |
|
| 1310 | /* One of an enumerator's basic member functions allows us to create a cloned
|
| 1311 | interface that initially has the same state. Since we are taking a snapshot
|
| 1312 | of an object (current position and all) we must lock access at the start */
|
| 1313 |
|
| 1314 | STDMETHODIMP
|
| 1315 | CEnumMediaTypes::Clone(__deref_out IEnumMediaTypes **ppEnum)
|
| 1316 | {
|
| 1317 | CheckPointer(ppEnum,E_POINTER);
|
| 1318 | ValidateReadWritePtr(ppEnum,sizeof(IEnumMediaTypes *));
|
| 1319 | HRESULT hr = NOERROR;
|
| 1320 |
|
| 1321 | /* Check we are still in sync with the pin */
|
| 1322 | if (AreWeOutOfSync() == TRUE) {
|
| 1323 | *ppEnum = NULL;
|
| 1324 | hr = VFW_E_ENUM_OUT_OF_SYNC;
|
| 1325 | } else {
|
| 1326 |
|
| 1327 | *ppEnum = new CEnumMediaTypes(m_pPin,
|
| 1328 | this);
|
| 1329 |
|
| 1330 | if (*ppEnum == NULL) {
|
| 1331 | hr = E_OUTOFMEMORY;
|
| 1332 | }
|
| 1333 | }
|
| 1334 | return hr;
|
| 1335 | }
|
| 1336 |
|
| 1337 |
|
| 1338 | /* Enumerate the next pin(s) after the current position. The client using this
|
| 1339 | interface passes in a pointer to an array of pointers each of which will
|
| 1340 | be filled in with a pointer to a fully initialised media type format
|
| 1341 | Return NOERROR if it all works,
|
| 1342 | S_FALSE if fewer than cMediaTypes were enumerated.
|
| 1343 | VFW_E_ENUM_OUT_OF_SYNC if the enumerator has been broken by
|
| 1344 | state changes in the filter
|
| 1345 | The actual count always correctly reflects the number of types in the array.
|
| 1346 | */
|
| 1347 |
|
| 1348 | STDMETHODIMP
|
| 1349 | CEnumMediaTypes::Next(ULONG cMediaTypes, // place this many types...
|
| 1350 | __out_ecount(cMediaTypes) AM_MEDIA_TYPE **ppMediaTypes, // ...in this array
|
| 1351 | __out ULONG *pcFetched) // actual count passed
|
| 1352 | {
|
| 1353 | CheckPointer(ppMediaTypes,E_POINTER);
|
| 1354 | ValidateReadWritePtr(ppMediaTypes,cMediaTypes * sizeof(AM_MEDIA_TYPE *));
|
| 1355 | /* Check we are still in sync with the pin */
|
| 1356 | if (AreWeOutOfSync() == TRUE) {
|
| 1357 | return VFW_E_ENUM_OUT_OF_SYNC;
|
| 1358 | }
|
| 1359 |
|
| 1360 | if (pcFetched!=NULL) {
|
| 1361 | ValidateWritePtr(pcFetched, sizeof(ULONG));
|
| 1362 | *pcFetched = 0; // default unless we succeed
|
| 1363 | }
|
| 1364 | // now check that the parameter is valid
|
| 1365 | else if (cMediaTypes>1) { // pcFetched == NULL
|
| 1366 | return E_INVALIDARG;
|
| 1367 | }
|
| 1368 | ULONG cFetched = 0; // increment as we get each one.
|
| 1369 |
|
| 1370 | /* Return each media type by asking the filter for them in turn - If we
|
| 1371 | have an error code retured to us while we are retrieving a media type
|
| 1372 | we assume that our internal state is stale with respect to the filter
|
| 1373 | (for example the window size changing) so we return
|
| 1374 | VFW_E_ENUM_OUT_OF_SYNC */
|
| 1375 |
|
| 1376 | while (cMediaTypes) {
|
| 1377 |
|
| 1378 | CMediaType cmt;
|
| 1379 |
|
| 1380 | HRESULT hr = m_pPin->GetMediaType(m_Position++, &cmt);
|
| 1381 | if (S_OK != hr) {
|
| 1382 | break;
|
| 1383 | }
|
| 1384 |
|
| 1385 | /* We now have a CMediaType object that contains the next media type
|
| 1386 | but when we assign it to the array position we CANNOT just assign
|
| 1387 | the AM_MEDIA_TYPE structure because as soon as the object goes out of
|
| 1388 | scope it will delete the memory we have just copied. The function
|
| 1389 | we use is CreateMediaType which allocates a task memory block */
|
| 1390 |
|
| 1391 | /* Transfer across the format block manually to save an allocate
|
| 1392 | and free on the format block and generally go faster */
|
| 1393 |
|
| 1394 | *ppMediaTypes = (AM_MEDIA_TYPE *)CoTaskMemAlloc(sizeof(AM_MEDIA_TYPE));
|
| 1395 | if (*ppMediaTypes == NULL) {
|
| 1396 | break;
|
| 1397 | }
|
| 1398 |
|
| 1399 | /* Do a regular copy */
|
| 1400 | **ppMediaTypes = cmt;
|
| 1401 |
|
| 1402 | /* Make sure the destructor doesn't free these */
|
| 1403 | cmt.pbFormat = NULL;
|
| 1404 | cmt.cbFormat = NULL;
|
| 1405 | cmt.pUnk = NULL;
|
| 1406 |
|
| 1407 |
|
| 1408 | ppMediaTypes++;
|
| 1409 | cFetched++;
|
| 1410 | cMediaTypes--;
|
| 1411 | }
|
| 1412 |
|
| 1413 | if (pcFetched!=NULL) {
|
| 1414 | *pcFetched = cFetched;
|
| 1415 | }
|
| 1416 |
|
| 1417 | return ( cMediaTypes==0 ? NOERROR : S_FALSE );
|
| 1418 | }
|
| 1419 |
|
| 1420 |
|
| 1421 | /* Skip over one or more entries in the enumerator */
|
| 1422 |
|
| 1423 | STDMETHODIMP
|
| 1424 | CEnumMediaTypes::Skip(ULONG cMediaTypes)
|
| 1425 | {
|
| 1426 | // If we're skipping 0 elements we're guaranteed to skip the
|
| 1427 | // correct number of elements
|
| 1428 | if (cMediaTypes == 0) {
|
| 1429 | return S_OK;
|
| 1430 | }
|
| 1431 |
|
| 1432 | /* Check we are still in sync with the pin */
|
| 1433 | if (AreWeOutOfSync() == TRUE) {
|
| 1434 | return VFW_E_ENUM_OUT_OF_SYNC;
|
| 1435 | }
|
| 1436 |
|
| 1437 | m_Position += cMediaTypes;
|
| 1438 |
|
| 1439 | /* See if we're over the end */
|
| 1440 | CMediaType cmt;
|
| 1441 | return S_OK == m_pPin->GetMediaType(m_Position - 1, &cmt) ? S_OK : S_FALSE;
|
| 1442 | }
|
| 1443 |
|
| 1444 |
|
| 1445 | /* Set the current position back to the start */
|
| 1446 | /* Reset has 3 simple steps:
|
| 1447 | *
|
| 1448 | * set position to head of list
|
| 1449 | * sync enumerator with object being enumerated
|
| 1450 | * return S_OK
|
| 1451 | */
|
| 1452 |
|
| 1453 | STDMETHODIMP
|
| 1454 | CEnumMediaTypes::Reset()
|
| 1455 |
|
| 1456 | {
|
| 1457 | m_Position = 0;
|
| 1458 |
|
| 1459 | // Bring the enumerator back into step with the current state. This
|
| 1460 | // may be a noop but ensures that the enumerator will be valid on the
|
| 1461 | // next call.
|
| 1462 | m_Version = m_pPin->GetMediaTypeVersion();
|
| 1463 | return NOERROR;
|
| 1464 | }
|
| 1465 |
|
| 1466 |
|
| 1467 | //=====================================================================
|
| 1468 | //=====================================================================
|
| 1469 | // Implements CBasePin
|
| 1470 | //=====================================================================
|
| 1471 | //=====================================================================
|
| 1472 |
|
| 1473 |
|
| 1474 | /* NOTE The implementation of this class calls the CUnknown constructor with
|
| 1475 | a NULL outer unknown pointer. This has the effect of making us a self
|
| 1476 | contained class, ie any QueryInterface, AddRef or Release calls will be
|
| 1477 | routed to the class's NonDelegatingUnknown methods. You will typically
|
| 1478 | find that the classes that do this then override one or more of these
|
| 1479 | virtual functions to provide more specialised behaviour. A good example
|
| 1480 | of this is where a class wants to keep the QueryInterface internal but
|
| 1481 | still wants its lifetime controlled by the external object */
|
| 1482 |
|
| 1483 | /* Constructor */
|
| 1484 |
|
| 1485 | CBasePin::CBasePin(__in_opt LPCTSTR pObjectName,
|
| 1486 | __in CBaseFilter *pFilter,
|
| 1487 | __in CCritSec *pLock,
|
| 1488 | __inout HRESULT *phr,
|
| 1489 | __in_opt LPCWSTR pName,
|
| 1490 | PIN_DIRECTION dir) :
|
| 1491 | CUnknown( pObjectName, NULL ),
|
| 1492 | m_pFilter(pFilter),
|
| 1493 | m_pLock(pLock),
|
| 1494 | m_pName(NULL),
|
| 1495 | m_Connected(NULL),
|
| 1496 | m_dir(dir),
|
| 1497 | m_bRunTimeError(FALSE),
|
| 1498 | m_pQSink(NULL),
|
| 1499 | m_TypeVersion(1),
|
| 1500 | m_tStart(),
|
| 1501 | m_tStop(MAX_TIME),
|
| 1502 | m_bCanReconnectWhenActive(false),
|
| 1503 | m_bTryMyTypesFirst(false),
|
| 1504 | m_dRate(1.0)
|
| 1505 | {
|
| 1506 | /* WARNING - pFilter is often not a properly constituted object at
|
| 1507 | this state (in particular QueryInterface may not work) - this
|
| 1508 | is because its owner is often its containing object and we
|
| 1509 | have been called from the containing object's constructor so
|
| 1510 | the filter's owner has not yet had its CUnknown constructor
|
| 1511 | called
|
| 1512 | */
|
| 1513 | #ifdef DXMPERF
|
| 1514 | PERFLOG_CTOR( pName ? pName : L"CBasePin", (IPin *) this );
|
| 1515 | #endif // DXMPERF
|
| 1516 |
|
| 1517 | ASSERT(pFilter != NULL);
|
| 1518 | ASSERT(pLock != NULL);
|
| 1519 |
|
| 1520 | if (pName) {
|
| 1521 | size_t cchName;
|
| 1522 | HRESULT hr = StringCchLengthW(pName, STRSAFE_MAX_CCH, &cchName);
|
| 1523 | if (SUCCEEDED(hr)) {
|
| 1524 | m_pName = new WCHAR[cchName + 1];
|
| 1525 | if (m_pName) {
|
| 1526 | (void)StringCchCopyW(m_pName, cchName + 1, pName);
|
| 1527 | }
|
| 1528 | }
|
| 1529 | }
|
| 1530 |
|
| 1531 | #ifdef DEBUG
|
| 1532 | m_cRef = 0;
|
| 1533 | #endif
|
| 1534 | }
|
| 1535 |
|
| 1536 | #ifdef UNICODE
|
| 1537 | CBasePin::CBasePin(__in_opt LPCSTR pObjectName,
|
| 1538 | __in CBaseFilter *pFilter,
|
| 1539 | __in CCritSec *pLock,
|
| 1540 | __inout HRESULT *phr,
|
| 1541 | __in_opt LPCWSTR pName,
|
| 1542 | PIN_DIRECTION dir) :
|
| 1543 | CUnknown( pObjectName, NULL ),
|
| 1544 | m_pFilter(pFilter),
|
| 1545 | m_pLock(pLock),
|
| 1546 | m_pName(NULL),
|
| 1547 | m_Connected(NULL),
|
| 1548 | m_dir(dir),
|
| 1549 | m_bRunTimeError(FALSE),
|
| 1550 | m_pQSink(NULL),
|
| 1551 | m_TypeVersion(1),
|
| 1552 | m_tStart(),
|
| 1553 | m_tStop(MAX_TIME),
|
| 1554 | m_bCanReconnectWhenActive(false),
|
| 1555 | m_bTryMyTypesFirst(false),
|
| 1556 | m_dRate(1.0)
|
| 1557 | {
|
| 1558 | /* WARNING - pFilter is often not a properly constituted object at
|
| 1559 | this state (in particular QueryInterface may not work) - this
|
| 1560 | is because its owner is often its containing object and we
|
| 1561 | have been called from the containing object's constructor so
|
| 1562 | the filter's owner has not yet had its CUnknown constructor
|
| 1563 | called
|
| 1564 | */
|
| 1565 | #ifdef DXMPERF
|
| 1566 | PERFLOG_CTOR( pName ? pName : L"CBasePin", (IPin *) this );
|
| 1567 | #endif // DXMPERF
|
| 1568 |
|
| 1569 | ASSERT(pFilter != NULL);
|
| 1570 | ASSERT(pLock != NULL);
|
| 1571 |
|
| 1572 | if (pName) {
|
| 1573 | size_t cchName;
|
| 1574 | HRESULT hr = StringCchLengthW(pName, STRSAFE_MAX_CCH, &cchName);
|
| 1575 | if (SUCCEEDED(hr)) {
|
| 1576 | m_pName = new WCHAR[cchName + 1];
|
| 1577 | if (m_pName) {
|
| 1578 | (void)StringCchCopyW(m_pName, cchName + 1, pName);
|
| 1579 | }
|
| 1580 | }
|
| 1581 | }
|
| 1582 |
|
| 1583 |
|
| 1584 | #ifdef DEBUG
|
| 1585 | m_cRef = 0;
|
| 1586 | #endif
|
| 1587 | }
|
| 1588 | #endif
|
| 1589 |
|
| 1590 | /* Destructor since a connected pin holds a reference count on us there is
|
| 1591 | no way that we can be deleted unless we are not currently connected */
|
| 1592 |
|
| 1593 | CBasePin::~CBasePin()
|
| 1594 | {
|
| 1595 | #ifdef DXMPERF
|
| 1596 | PERFLOG_DTOR( m_pName ? m_pName : L"CBasePin", (IPin *) this );
|
| 1597 | #endif // DXMPERF
|
| 1598 |
|
| 1599 | // We don't call disconnect because if the filter is going away
|
| 1600 | // all the pins must have a reference count of zero so they must
|
| 1601 | // have been disconnected anyway - (but check the assumption)
|
| 1602 | ASSERT(m_Connected == FALSE);
|
| 1603 |
|
| 1604 | delete[] m_pName;
|
| 1605 |
|
| 1606 | // check the internal reference count is consistent
|
| 1607 | ASSERT(m_cRef == 0);
|
| 1608 | }
|
| 1609 |
|
| 1610 |
|
| 1611 | /* Override this to say what interfaces we support and where */
|
| 1612 |
|
| 1613 | STDMETHODIMP
|
| 1614 | CBasePin::NonDelegatingQueryInterface(REFIID riid, __deref_out void ** ppv)
|
| 1615 | {
|
| 1616 | /* Do we have this interface */
|
| 1617 |
|
| 1618 | if (riid == IID_IPin) {
|
| 1619 | return GetInterface((IPin *) this, ppv);
|
| 1620 | } else if (riid == IID_IQualityControl) {
|
| 1621 | return GetInterface((IQualityControl *) this, ppv);
|
| 1622 | } else {
|
| 1623 | return CUnknown::NonDelegatingQueryInterface(riid, ppv);
|
| 1624 | }
|
| 1625 | }
|
| 1626 |
|
| 1627 |
|
| 1628 | /* Override to increment the owning filter's reference count */
|
| 1629 |
|
| 1630 | STDMETHODIMP_(ULONG)
|
| 1631 | CBasePin::NonDelegatingAddRef()
|
| 1632 | {
|
| 1633 | ASSERT(InterlockedIncrement(&m_cRef) > 0);
|
| 1634 | return m_pFilter->AddRef();
|
| 1635 | }
|
| 1636 |
|
| 1637 |
|
| 1638 | /* Override to decrement the owning filter's reference count */
|
| 1639 |
|
| 1640 | STDMETHODIMP_(ULONG)
|
| 1641 | CBasePin::NonDelegatingRelease()
|
| 1642 | {
|
| 1643 | ASSERT(InterlockedDecrement(&m_cRef) >= 0);
|
| 1644 | return m_pFilter->Release();
|
| 1645 | }
|
| 1646 |
|
| 1647 |
|
| 1648 | /* Displays pin connection information */
|
| 1649 |
|
| 1650 | #ifdef DEBUG
|
| 1651 | void
|
| 1652 | CBasePin::DisplayPinInfo(IPin *pReceivePin)
|
| 1653 | {
|
| 1654 |
|
| 1655 | if (DbgCheckModuleLevel(LOG_TRACE, CONNECT_TRACE_LEVEL)) {
|
| 1656 | PIN_INFO ConnectPinInfo;
|
| 1657 | PIN_INFO ReceivePinInfo;
|
| 1658 |
|
| 1659 | if (FAILED(QueryPinInfo(&ConnectPinInfo))) {
|
| 1660 | StringCchCopyW(ConnectPinInfo.achName, sizeof(ConnectPinInfo.achName)/sizeof(WCHAR), L"Bad Pin");
|
| 1661 | } else {
|
| 1662 | QueryPinInfoReleaseFilter(ConnectPinInfo);
|
| 1663 | }
|
| 1664 |
|
| 1665 | if (FAILED(pReceivePin->QueryPinInfo(&ReceivePinInfo))) {
|
| 1666 | StringCchCopyW(ReceivePinInfo.achName, sizeof(ReceivePinInfo.achName)/sizeof(WCHAR), L"Bad Pin");
|
| 1667 | } else {
|
| 1668 | QueryPinInfoReleaseFilter(ReceivePinInfo);
|
| 1669 | }
|
| 1670 |
|
| 1671 | DbgLog((LOG_TRACE, CONNECT_TRACE_LEVEL, TEXT("Trying to connect Pins :")));
|
| 1672 | DbgLog((LOG_TRACE, CONNECT_TRACE_LEVEL, TEXT(" <%ls>"), ConnectPinInfo.achName));
|
| 1673 | DbgLog((LOG_TRACE, CONNECT_TRACE_LEVEL, TEXT(" <%ls>"), ReceivePinInfo.achName));
|
| 1674 | }
|
| 1675 | }
|
| 1676 | #endif
|
| 1677 |
|
| 1678 |
|
| 1679 | /* Displays general information on the pin media type */
|
| 1680 |
|
| 1681 | #ifdef DEBUG
|
| 1682 | void CBasePin::DisplayTypeInfo(IPin *pPin, const CMediaType *pmt)
|
| 1683 | {
|
| 1684 | UNREFERENCED_PARAMETER(pPin);
|
| 1685 | if (DbgCheckModuleLevel(LOG_TRACE, CONNECT_TRACE_LEVEL)) {
|
| 1686 | DbgLog((LOG_TRACE, CONNECT_TRACE_LEVEL, TEXT("Trying media type:")));
|
| 1687 | DbgLog((LOG_TRACE, CONNECT_TRACE_LEVEL, TEXT(" major type: %hs"),
|
| 1688 | GuidNames[*pmt->Type()]));
|
| 1689 | DbgLog((LOG_TRACE, CONNECT_TRACE_LEVEL, TEXT(" sub type : %hs"),
|
| 1690 | GuidNames[*pmt->Subtype()]));
|
| 1691 | }
|
| 1692 | }
|
| 1693 | #endif
|
| 1694 |
|
| 1695 | /* Asked to connect to a pin. A pin is always attached to an owning filter
|
| 1696 | object so we always delegate our locking to that object. We first of all
|
| 1697 | retrieve a media type enumerator for the input pin and see if we accept
|
| 1698 | any of the formats that it would ideally like, failing that we retrieve
|
| 1699 | our enumerator and see if it will accept any of our preferred types */
|
| 1700 |
|
| 1701 | STDMETHODIMP
|
| 1702 | CBasePin::Connect(
|
| 1703 | IPin * pReceivePin,
|
| 1704 | __in_opt const AM_MEDIA_TYPE *pmt // optional media type
|
| 1705 | )
|
| 1706 | {
|
| 1707 | CheckPointer(pReceivePin,E_POINTER);
|
| 1708 | ValidateReadPtr(pReceivePin,sizeof(IPin));
|
| 1709 | CAutoLock cObjectLock(m_pLock);
|
| 1710 | DisplayPinInfo(pReceivePin);
|
| 1711 |
|
| 1712 | /* See if we are already connected */
|
| 1713 |
|
| 1714 | if (m_Connected) {
|
| 1715 | DbgLog((LOG_TRACE, CONNECT_TRACE_LEVEL, TEXT("Already connected")));
|
| 1716 | return VFW_E_ALREADY_CONNECTED;
|
| 1717 | }
|
| 1718 |
|
| 1719 | /* See if the filter is active */
|
| 1720 | if (!IsStopped() && !m_bCanReconnectWhenActive) {
|
| 1721 | return VFW_E_NOT_STOPPED;
|
| 1722 | }
|
| 1723 |
|
| 1724 |
|
| 1725 | // Find a mutually agreeable media type -
|
| 1726 | // Pass in the template media type. If this is partially specified,
|
| 1727 | // each of the enumerated media types will need to be checked against
|
| 1728 | // it. If it is non-null and fully specified, we will just try to connect
|
| 1729 | // with this.
|
| 1730 |
|
| 1731 | const CMediaType * ptype = (CMediaType*)pmt;
|
| 1732 | HRESULT hr = AgreeMediaType(pReceivePin, ptype);
|
| 1733 | if (FAILED(hr)) {
|
| 1734 | DbgLog((LOG_TRACE, CONNECT_TRACE_LEVEL, TEXT("Failed to agree type")));
|
| 1735 |
|
| 1736 | // Since the procedure is already returning an error code, there
|
| 1737 | // is nothing else this function can do to report the error.
|
| 1738 | EXECUTE_ASSERT( SUCCEEDED( BreakConnect() ) );
|
| 1739 |
|
| 1740 | #ifdef DXMPERF
|
| 1741 | PERFLOG_CONNECT( (IPin *) this, pReceivePin, hr, pmt );
|
| 1742 | #endif // DXMPERF
|
| 1743 |
|
| 1744 | return hr;
|
| 1745 | }
|
| 1746 |
|
| 1747 | DbgLog((LOG_TRACE, CONNECT_TRACE_LEVEL, TEXT("Connection succeeded")));
|
| 1748 |
|
| 1749 | #ifdef DXMPERF
|
| 1750 | PERFLOG_CONNECT( (IPin *) this, pReceivePin, NOERROR, pmt );
|
| 1751 | #endif // DXMPERF
|
| 1752 |
|
| 1753 | return NOERROR;
|
| 1754 | }
|
| 1755 |
|
| 1756 | // given a specific media type, attempt a connection (includes
|
| 1757 | // checking that the type is acceptable to this pin)
|
| 1758 | HRESULT
|
| 1759 | CBasePin::AttemptConnection(
|
| 1760 | IPin* pReceivePin, // connect to this pin
|
| 1761 | const CMediaType* pmt // using this type
|
| 1762 | )
|
| 1763 | {
|
| 1764 | // The caller should hold the filter lock becasue this function
|
| 1765 | // uses m_Connected. The caller should also hold the filter lock
|
| 1766 | // because this function calls SetMediaType(), IsStopped() and
|
| 1767 | // CompleteConnect().
|
| 1768 | ASSERT(CritCheckIn(m_pLock));
|
| 1769 |
|
| 1770 | // Check that the connection is valid -- need to do this for every
|
| 1771 | // connect attempt since BreakConnect will undo it.
|
| 1772 | HRESULT hr = CheckConnect(pReceivePin);
|
| 1773 | if (FAILED(hr)) {
|
| 1774 | DbgLog((LOG_TRACE, CONNECT_TRACE_LEVEL, TEXT("CheckConnect failed")));
|
| 1775 |
|
| 1776 | // Since the procedure is already returning an error code, there
|
| 1777 | // is nothing else this function can do to report the error.
|
| 1778 | EXECUTE_ASSERT( SUCCEEDED( BreakConnect() ) );
|
| 1779 |
|
| 1780 | return hr;
|
| 1781 | }
|
| 1782 |
|
| 1783 | DisplayTypeInfo(pReceivePin, pmt);
|
| 1784 |
|
| 1785 | /* Check we will accept this media type */
|
| 1786 |
|
| 1787 | hr = CheckMediaType(pmt);
|
| 1788 | if (hr == NOERROR) {
|
| 1789 |
|
| 1790 | /* Make ourselves look connected otherwise ReceiveConnection
|
| 1791 | may not be able to complete the connection
|
| 1792 | */
|
| 1793 | m_Connected = pReceivePin;
|
| 1794 | m_Connected->AddRef();
|
| 1795 | hr = SetMediaType(pmt);
|
| 1796 | if (SUCCEEDED(hr)) {
|
| 1797 | /* See if the other pin will accept this type */
|
| 1798 |
|
| 1799 | hr = pReceivePin->ReceiveConnection((IPin *)this, pmt);
|
| 1800 | if (SUCCEEDED(hr)) {
|
| 1801 | /* Complete the connection */
|
| 1802 |
|
| 1803 | hr = CompleteConnect(pReceivePin);
|
| 1804 | if (SUCCEEDED(hr)) {
|
| 1805 | return hr;
|
| 1806 | } else {
|
| 1807 | DbgLog((LOG_TRACE,
|
| 1808 | CONNECT_TRACE_LEVEL,
|
| 1809 | TEXT("Failed to complete connection")));
|
| 1810 | pReceivePin->Disconnect();
|
| 1811 | }
|
| 1812 | }
|
| 1813 | }
|
| 1814 | } else {
|
| 1815 | // we cannot use this media type
|
| 1816 |
|
| 1817 | // return a specific media type error if there is one
|
| 1818 | // or map a general failure code to something more helpful
|
| 1819 | // (in particular S_FALSE gets changed to an error code)
|
| 1820 | if (SUCCEEDED(hr) ||
|
| 1821 | (hr == E_FAIL) ||
|
| 1822 | (hr == E_INVALIDARG)) {
|
| 1823 | hr = VFW_E_TYPE_NOT_ACCEPTED;
|
| 1824 | }
|
| 1825 | }
|
| 1826 |
|
| 1827 | // BreakConnect and release any connection here in case CheckMediaType
|
| 1828 | // failed, or if we set anything up during a call back during
|
| 1829 | // ReceiveConnection.
|
| 1830 |
|
| 1831 | // Since the procedure is already returning an error code, there
|
| 1832 | // is nothing else this function can do to report the error.
|
| 1833 | EXECUTE_ASSERT( SUCCEEDED( BreakConnect() ) );
|
| 1834 |
|
| 1835 | /* If failed then undo our state */
|
| 1836 | if (m_Connected) {
|
| 1837 | m_Connected->Release();
|
| 1838 | m_Connected = NULL;
|
| 1839 | }
|
| 1840 |
|
| 1841 | return hr;
|
| 1842 | }
|
| 1843 |
|
| 1844 | /* Given an enumerator we cycle through all the media types it proposes and
|
| 1845 | firstly suggest them to our derived pin class and if that succeeds try
|
| 1846 | them with the pin in a ReceiveConnection call. This means that if our pin
|
| 1847 | proposes a media type we still check in here that we can support it. This
|
| 1848 | is deliberate so that in simple cases the enumerator can hold all of the
|
| 1849 | media types even if some of them are not really currently available */
|
| 1850 |
|
| 1851 | HRESULT CBasePin::TryMediaTypes(
|
| 1852 | IPin *pReceivePin,
|
| 1853 | __in_opt const CMediaType *pmt,
|
| 1854 | IEnumMediaTypes *pEnum)
|
| 1855 | {
|
| 1856 | /* Reset the current enumerator position */
|
| 1857 |
|
| 1858 | HRESULT hr = pEnum->Reset();
|
| 1859 | if (FAILED(hr)) {
|
| 1860 | return hr;
|
| 1861 | }
|
| 1862 |
|
| 1863 | CMediaType *pMediaType = NULL;
|
| 1864 | ULONG ulMediaCount = 0;
|
| 1865 |
|
| 1866 | // attempt to remember a specific error code if there is one
|
| 1867 | HRESULT hrFailure = S_OK;
|
| 1868 |
|
| 1869 | for (;;) {
|
| 1870 |
|
| 1871 | /* Retrieve the next media type NOTE each time round the loop the
|
| 1872 | enumerator interface will allocate another AM_MEDIA_TYPE structure
|
| 1873 | If we are successful then we copy it into our output object, if
|
| 1874 | not then we must delete the memory allocated before returning */
|
| 1875 |
|
| 1876 | hr = pEnum->Next(1, (AM_MEDIA_TYPE**)&pMediaType,&ulMediaCount);
|
| 1877 | if (hr != S_OK) {
|
| 1878 | if (S_OK == hrFailure) {
|
| 1879 | hrFailure = VFW_E_NO_ACCEPTABLE_TYPES;
|
| 1880 | }
|
| 1881 | return hrFailure;
|
| 1882 | }
|
| 1883 |
|
| 1884 |
|
| 1885 | ASSERT(ulMediaCount == 1);
|
| 1886 | ASSERT(pMediaType);
|
| 1887 |
|
| 1888 | // check that this matches the partial type (if any)
|
| 1889 |
|
| 1890 | if (pMediaType &&
|
| 1891 | ((pmt == NULL) ||
|
| 1892 | pMediaType->MatchesPartial(pmt))) {
|
| 1893 |
|
| 1894 | hr = AttemptConnection(pReceivePin, pMediaType);
|
| 1895 |
|
| 1896 | // attempt to remember a specific error code
|
| 1897 | if (FAILED(hr) &&
|
| 1898 | SUCCEEDED(hrFailure) &&
|
| 1899 | (hr != E_FAIL) &&
|
| 1900 | (hr != E_INVALIDARG) &&
|
| 1901 | (hr != VFW_E_TYPE_NOT_ACCEPTED)) {
|
| 1902 | hrFailure = hr;
|
| 1903 | }
|
| 1904 | } else {
|
| 1905 | hr = VFW_E_NO_ACCEPTABLE_TYPES;
|
| 1906 | }
|
| 1907 |
|
| 1908 | if(pMediaType) {
|
| 1909 | DeleteMediaType(pMediaType);
|
| 1910 | pMediaType = NULL;
|
| 1911 | }
|
| 1912 |
|
| 1913 | if (S_OK == hr) {
|
| 1914 | return hr;
|
| 1915 | }
|
| 1916 | }
|
| 1917 | }
|
| 1918 |
|
| 1919 |
|
| 1920 | /* This is called to make the connection, including the taask of finding
|
| 1921 | a media type for the pin connection. pmt is the proposed media type
|
| 1922 | from the Connect call: if this is fully specified, we will try that.
|
| 1923 | Otherwise we enumerate and try all the input pin's types first and
|
| 1924 | if that fails we then enumerate and try all our preferred media types.
|
| 1925 | For each media type we check it against pmt (if non-null and partially
|
| 1926 | specified) as well as checking that both pins will accept it.
|
| 1927 | */
|
| 1928 |
|
| 1929 | HRESULT CBasePin::AgreeMediaType(
|
| 1930 | IPin *pReceivePin,
|
| 1931 | const CMediaType *pmt)
|
| 1932 | {
|
| 1933 | ASSERT(pReceivePin);
|
| 1934 | IEnumMediaTypes *pEnumMediaTypes = NULL;
|
| 1935 |
|
| 1936 | // if the media type is fully specified then use that
|
| 1937 | if ( (pmt != NULL) && (!pmt->IsPartiallySpecified())) {
|
| 1938 |
|
| 1939 | // if this media type fails, then we must fail the connection
|
| 1940 | // since if pmt is nonnull we are only allowed to connect
|
| 1941 | // using a type that matches it.
|
| 1942 |
|
| 1943 | return AttemptConnection(pReceivePin, pmt);
|
| 1944 | }
|
| 1945 |
|
| 1946 |
|
| 1947 | /* Try the other pin's enumerator */
|
| 1948 |
|
| 1949 | HRESULT hrFailure = VFW_E_NO_ACCEPTABLE_TYPES;
|
| 1950 |
|
| 1951 | for (int i = 0; i < 2; i++) {
|
| 1952 | HRESULT hr;
|
| 1953 | if (i == (int)m_bTryMyTypesFirst) {
|
| 1954 | hr = pReceivePin->EnumMediaTypes(&pEnumMediaTypes);
|
| 1955 | } else {
|
| 1956 | hr = EnumMediaTypes(&pEnumMediaTypes);
|
| 1957 | }
|
| 1958 | if (SUCCEEDED(hr)) {
|
| 1959 | ASSERT(pEnumMediaTypes);
|
| 1960 | hr = TryMediaTypes(pReceivePin,pmt,pEnumMediaTypes);
|
| 1961 | pEnumMediaTypes->Release();
|
| 1962 | if (SUCCEEDED(hr)) {
|
| 1963 | return NOERROR;
|
| 1964 | } else {
|
| 1965 | // try to remember specific error codes if there are any
|
| 1966 | if ((hr != E_FAIL) &&
|
| 1967 | (hr != E_INVALIDARG) &&
|
| 1968 | (hr != VFW_E_TYPE_NOT_ACCEPTED)) {
|
| 1969 | hrFailure = hr;
|
| 1970 | }
|
| 1971 | }
|
| 1972 | }
|
| 1973 | }
|
| 1974 |
|
| 1975 | return hrFailure;
|
| 1976 | }
|
| 1977 |
|
| 1978 |
|
| 1979 | /* Called when we want to complete a connection to another filter. Failing
|
| 1980 | this will also fail the connection and disconnect the other pin as well */
|
| 1981 |
|
| 1982 | HRESULT
|
| 1983 | CBasePin::CompleteConnect(IPin *pReceivePin)
|
| 1984 | {
|
| 1985 | UNREFERENCED_PARAMETER(pReceivePin);
|
| 1986 | return NOERROR;
|
| 1987 | }
|
| 1988 |
|
| 1989 |
|
| 1990 | /* This is called to set the format for a pin connection - CheckMediaType
|
| 1991 | will have been called to check the connection format and if it didn't
|
| 1992 | return an error code then this (virtual) function will be invoked */
|
| 1993 |
|
| 1994 | HRESULT
|
| 1995 | CBasePin::SetMediaType(const CMediaType *pmt)
|
| 1996 | {
|
| 1997 | HRESULT hr = m_mt.Set(*pmt);
|
| 1998 | if (FAILED(hr)) {
|
| 1999 | return hr;
|
| 2000 | }
|
| 2001 |
|
| 2002 | return NOERROR;
|
| 2003 | }
|
| 2004 |
|
| 2005 |
|
| 2006 | /* This is called during Connect() to provide a virtual method that can do
|
| 2007 | any specific check needed for connection such as QueryInterface. This
|
| 2008 | base class method just checks that the pin directions don't match */
|
| 2009 |
|
| 2010 | HRESULT
|
| 2011 | CBasePin::CheckConnect(IPin * pPin)
|
| 2012 | {
|
| 2013 | /* Check that pin directions DONT match */
|
| 2014 |
|
| 2015 | PIN_DIRECTION pd;
|
| 2016 | pPin->QueryDirection(&pd);
|
| 2017 |
|
| 2018 | ASSERT((pd == PINDIR_OUTPUT) || (pd == PINDIR_INPUT));
|
| 2019 | ASSERT((m_dir == PINDIR_OUTPUT) || (m_dir == PINDIR_INPUT));
|
| 2020 |
|
| 2021 | // we should allow for non-input and non-output connections?
|
| 2022 | if (pd == m_dir) {
|
| 2023 | return VFW_E_INVALID_DIRECTION;
|
| 2024 | }
|
| 2025 | return NOERROR;
|
| 2026 | }
|
| 2027 |
|
| 2028 |
|
| 2029 | /* This is called when we realise we can't make a connection to the pin and
|
| 2030 | must undo anything we did in CheckConnect - override to release QIs done */
|
| 2031 |
|
| 2032 | HRESULT
|
| 2033 | CBasePin::BreakConnect()
|
| 2034 | {
|
| 2035 | return NOERROR;
|
| 2036 | }
|
| 2037 |
|
| 2038 |
|
| 2039 | /* Called normally by an output pin on an input pin to try and establish a
|
| 2040 | connection.
|
| 2041 | */
|
| 2042 |
|
| 2043 | STDMETHODIMP
|
| 2044 | CBasePin::ReceiveConnection(
|
| 2045 | IPin * pConnector, // this is the pin who we will connect to
|
| 2046 | const AM_MEDIA_TYPE *pmt // this is the media type we will exchange
|
| 2047 | )
|
| 2048 | {
|
| 2049 | CheckPointer(pConnector,E_POINTER);
|
| 2050 | CheckPointer(pmt,E_POINTER);
|
| 2051 | ValidateReadPtr(pConnector,sizeof(IPin));
|
| 2052 | ValidateReadPtr(pmt,sizeof(AM_MEDIA_TYPE));
|
| 2053 | CAutoLock cObjectLock(m_pLock);
|
| 2054 |
|
| 2055 | /* Are we already connected */
|
| 2056 | if (m_Connected) {
|
| 2057 | return VFW_E_ALREADY_CONNECTED;
|
| 2058 | }
|
| 2059 |
|
| 2060 | /* See if the filter is active */
|
| 2061 | if (!IsStopped() && !m_bCanReconnectWhenActive) {
|
| 2062 | return VFW_E_NOT_STOPPED;
|
| 2063 | }
|
| 2064 |
|
| 2065 | HRESULT hr = CheckConnect(pConnector);
|
| 2066 | if (FAILED(hr)) {
|
| 2067 | // Since the procedure is already returning an error code, there
|
| 2068 | // is nothing else this function can do to report the error.
|
| 2069 | EXECUTE_ASSERT( SUCCEEDED( BreakConnect() ) );
|
| 2070 |
|
| 2071 | #ifdef DXMPERF
|
| 2072 | PERFLOG_RXCONNECT( pConnector, (IPin *) this, hr, pmt );
|
| 2073 | #endif // DXMPERF
|
| 2074 |
|
| 2075 | return hr;
|
| 2076 | }
|
| 2077 |
|
| 2078 | /* Ask derived class if this media type is ok */
|
| 2079 |
|
| 2080 | CMediaType * pcmt = (CMediaType*) pmt;
|
| 2081 | hr = CheckMediaType(pcmt);
|
| 2082 | if (hr != NOERROR) {
|
| 2083 | // no -we don't support this media type
|
| 2084 |
|
| 2085 | // Since the procedure is already returning an error code, there
|
| 2086 | // is nothing else this function can do to report the error.
|
| 2087 | EXECUTE_ASSERT( SUCCEEDED( BreakConnect() ) );
|
| 2088 |
|
| 2089 | // return a specific media type error if there is one
|
| 2090 | // or map a general failure code to something more helpful
|
| 2091 | // (in particular S_FALSE gets changed to an error code)
|
| 2092 | if (SUCCEEDED(hr) ||
|
| 2093 | (hr == E_FAIL) ||
|
| 2094 | (hr == E_INVALIDARG)) {
|
| 2095 | hr = VFW_E_TYPE_NOT_ACCEPTED;
|
| 2096 | }
|
| 2097 |
|
| 2098 | #ifdef DXMPERF
|
| 2099 | PERFLOG_RXCONNECT( pConnector, (IPin *) this, hr, pmt );
|
| 2100 | #endif // DXMPERF
|
| 2101 |
|
| 2102 | return hr;
|
| 2103 | }
|
| 2104 |
|
| 2105 | /* Complete the connection */
|
| 2106 |
|
| 2107 | m_Connected = pConnector;
|
| 2108 | m_Connected->AddRef();
|
| 2109 | hr = SetMediaType(pcmt);
|
| 2110 | if (SUCCEEDED(hr)) {
|
| 2111 | hr = CompleteConnect(pConnector);
|
| 2112 | if (SUCCEEDED(hr)) {
|
| 2113 |
|
| 2114 | #ifdef DXMPERF
|
| 2115 | PERFLOG_RXCONNECT( pConnector, (IPin *) this, NOERROR, pmt );
|
| 2116 | #endif // DXMPERF
|
| 2117 |
|
| 2118 | return NOERROR;
|
| 2119 | }
|
| 2120 | }
|
| 2121 |
|
| 2122 | DbgLog((LOG_TRACE, CONNECT_TRACE_LEVEL, TEXT("Failed to set the media type or failed to complete the connection.")));
|
| 2123 | m_Connected->Release();
|
| 2124 | m_Connected = NULL;
|
| 2125 |
|
| 2126 | // Since the procedure is already returning an error code, there
|
| 2127 | // is nothing else this function can do to report the error.
|
| 2128 | EXECUTE_ASSERT( SUCCEEDED( BreakConnect() ) );
|
| 2129 |
|
| 2130 | #ifdef DXMPERF
|
| 2131 | PERFLOG_RXCONNECT( pConnector, (IPin *) this, hr, pmt );
|
| 2132 | #endif // DXMPERF
|
| 2133 |
|
| 2134 | return hr;
|
| 2135 | }
|
| 2136 |
|
| 2137 |
|
| 2138 | /* Called when we want to terminate a pin connection */
|
| 2139 |
|
| 2140 | STDMETHODIMP
|
| 2141 | CBasePin::Disconnect()
|
| 2142 | {
|
| 2143 | CAutoLock cObjectLock(m_pLock);
|
| 2144 |
|
| 2145 | /* See if the filter is active */
|
| 2146 | if (!IsStopped()) {
|
| 2147 | return VFW_E_NOT_STOPPED;
|
| 2148 | }
|
| 2149 |
|
| 2150 | return DisconnectInternal();
|
| 2151 | }
|
| 2152 |
|
| 2153 | STDMETHODIMP
|
| 2154 | CBasePin::DisconnectInternal()
|
| 2155 | {
|
| 2156 | ASSERT(CritCheckIn(m_pLock));
|
| 2157 |
|
| 2158 | if (m_Connected) {
|
| 2159 | HRESULT hr = BreakConnect();
|
| 2160 | if( FAILED( hr ) ) {
|
| 2161 |
|
| 2162 | #ifdef DXMPERF
|
| 2163 | PERFLOG_DISCONNECT( (IPin *) this, m_Connected, hr );
|
| 2164 | #endif // DXMPERF
|
| 2165 |
|
| 2166 | // There is usually a bug in the program if BreakConnect() fails.
|
| 2167 | DbgBreak( "WARNING: BreakConnect() failed in CBasePin::Disconnect()." );
|
| 2168 | return hr;
|
| 2169 | }
|
| 2170 |
|
| 2171 | m_Connected->Release();
|
| 2172 | m_Connected = NULL;
|
| 2173 |
|
| 2174 | #ifdef DXMPERF
|
| 2175 | PERFLOG_DISCONNECT( (IPin *) this, m_Connected, S_OK );
|
| 2176 | #endif // DXMPERF
|
| 2177 |
|
| 2178 | return S_OK;
|
| 2179 | } else {
|
| 2180 | // no connection - not an error
|
| 2181 |
|
| 2182 | #ifdef DXMPERF
|
| 2183 | PERFLOG_DISCONNECT( (IPin *) this, m_Connected, S_FALSE );
|
| 2184 | #endif // DXMPERF
|
| 2185 |
|
| 2186 | return S_FALSE;
|
| 2187 | }
|
| 2188 | }
|
| 2189 |
|
| 2190 |
|
| 2191 | /* Return an AddRef()'d pointer to the connected pin if there is one */
|
| 2192 | STDMETHODIMP
|
| 2193 | CBasePin::ConnectedTo(
|
| 2194 | __deref_out IPin **ppPin
|
| 2195 | )
|
| 2196 | {
|
| 2197 | CheckPointer(ppPin,E_POINTER);
|
| 2198 | ValidateReadWritePtr(ppPin,sizeof(IPin *));
|
| 2199 | //
|
| 2200 | // It's pointless to lock here.
|
| 2201 | // The caller should ensure integrity.
|
| 2202 | //
|
| 2203 |
|
| 2204 | IPin *pPin = m_Connected;
|
| 2205 | *ppPin = pPin;
|
| 2206 | if (pPin != NULL) {
|
| 2207 | pPin->AddRef();
|
| 2208 | return S_OK;
|
| 2209 | } else {
|
| 2210 | ASSERT(*ppPin == NULL);
|
| 2211 | return VFW_E_NOT_CONNECTED;
|
| 2212 | }
|
| 2213 | }
|
| 2214 |
|
| 2215 | /* Return the media type of the connection */
|
| 2216 | STDMETHODIMP
|
| 2217 | CBasePin::ConnectionMediaType(
|
| 2218 | __out AM_MEDIA_TYPE *pmt
|
| 2219 | )
|
| 2220 | {
|
| 2221 | CheckPointer(pmt,E_POINTER);
|
| 2222 | ValidateReadWritePtr(pmt,sizeof(AM_MEDIA_TYPE));
|
| 2223 | CAutoLock cObjectLock(m_pLock);
|
| 2224 |
|
| 2225 | /* Copy constructor of m_mt allocates the memory */
|
| 2226 | if (IsConnected()) {
|
| 2227 | CopyMediaType( pmt, &m_mt );
|
| 2228 | return S_OK;
|
| 2229 | } else {
|
| 2230 | ((CMediaType *)pmt)->InitMediaType();
|
| 2231 | return VFW_E_NOT_CONNECTED;
|
| 2232 | }
|
| 2233 | }
|
| 2234 |
|
| 2235 | /* Return information about the filter we are connect to */
|
| 2236 |
|
| 2237 | STDMETHODIMP
|
| 2238 | CBasePin::QueryPinInfo(
|
| 2239 | __out PIN_INFO * pInfo
|
| 2240 | )
|
| 2241 | {
|
| 2242 | CheckPointer(pInfo,E_POINTER);
|
| 2243 | ValidateReadWritePtr(pInfo,sizeof(PIN_INFO));
|
| 2244 |
|
| 2245 | pInfo->pFilter = m_pFilter;
|
| 2246 | if (m_pFilter) {
|
| 2247 | m_pFilter->AddRef();
|
| 2248 | }
|
| 2249 |
|
| 2250 | if (m_pName) {
|
| 2251 | (void)StringCchCopyW(pInfo->achName, NUMELMS(pInfo->achName), m_pName);
|
| 2252 | } else {
|
| 2253 | pInfo->achName[0] = L'\0';
|
| 2254 | }
|
| 2255 |
|
| 2256 | pInfo->dir = m_dir;
|
| 2257 |
|
| 2258 | return NOERROR;
|
| 2259 | }
|
| 2260 |
|
| 2261 | STDMETHODIMP
|
| 2262 | CBasePin::QueryDirection(
|
| 2263 | __out PIN_DIRECTION * pPinDir
|
| 2264 | )
|
| 2265 | {
|
| 2266 | CheckPointer(pPinDir,E_POINTER);
|
| 2267 | ValidateReadWritePtr(pPinDir,sizeof(PIN_DIRECTION));
|
| 2268 |
|
| 2269 | *pPinDir = m_dir;
|
| 2270 | return NOERROR;
|
| 2271 | }
|
| 2272 |
|
| 2273 | // Default QueryId to return the pin's name
|
| 2274 | STDMETHODIMP
|
| 2275 | CBasePin::QueryId(
|
| 2276 | __deref_out LPWSTR * Id
|
| 2277 | )
|
| 2278 | {
|
| 2279 | // We're not going away because someone's got a pointer to us
|
| 2280 | // so there's no need to lock
|
| 2281 |
|
| 2282 | return AMGetWideString(Name(), Id);
|
| 2283 | }
|
| 2284 |
|
| 2285 | /* Does this pin support this media type WARNING this interface function does
|
| 2286 | not lock the main object as it is meant to be asynchronous by nature - if
|
| 2287 | the media types you support depend on some internal state that is updated
|
| 2288 | dynamically then you will need to implement locking in a derived class */
|
| 2289 |
|
| 2290 | STDMETHODIMP
|
| 2291 | CBasePin::QueryAccept(
|
| 2292 | const AM_MEDIA_TYPE *pmt
|
| 2293 | )
|
| 2294 | {
|
| 2295 | CheckPointer(pmt,E_POINTER);
|
| 2296 | ValidateReadPtr(pmt,sizeof(AM_MEDIA_TYPE));
|
| 2297 |
|
| 2298 | /* The CheckMediaType method is valid to return error codes if the media
|
| 2299 | type is horrible, an example might be E_INVALIDARG. What we do here
|
| 2300 | is map all the error codes into either S_OK or S_FALSE regardless */
|
| 2301 |
|
| 2302 | HRESULT hr = CheckMediaType((CMediaType*)pmt);
|
| 2303 | if (FAILED(hr)) {
|
| 2304 | return S_FALSE;
|
| 2305 | }
|
| 2306 | // note that the only defined success codes should be S_OK and S_FALSE...
|
| 2307 | return hr;
|
| 2308 | }
|
| 2309 |
|
| 2310 |
|
| 2311 | /* This can be called to return an enumerator for the pin's list of preferred
|
| 2312 | media types. An input pin is not obliged to have any preferred formats
|
| 2313 | although it can do. For example, the window renderer has a preferred type
|
| 2314 | which describes a video image that matches the current window size. All
|
| 2315 | output pins should expose at least one preferred format otherwise it is
|
| 2316 | possible that neither pin has any types and so no connection is possible */
|
| 2317 |
|
| 2318 | STDMETHODIMP
|
| 2319 | CBasePin::EnumMediaTypes(
|
| 2320 | __deref_out IEnumMediaTypes **ppEnum
|
| 2321 | )
|
| 2322 | {
|
| 2323 | CheckPointer(ppEnum,E_POINTER);
|
| 2324 | ValidateReadWritePtr(ppEnum,sizeof(IEnumMediaTypes *));
|
| 2325 |
|
| 2326 | /* Create a new ref counted enumerator */
|
| 2327 |
|
| 2328 | *ppEnum = new CEnumMediaTypes(this,
|
| 2329 | NULL);
|
| 2330 |
|
| 2331 | if (*ppEnum == NULL) {
|
| 2332 | return E_OUTOFMEMORY;
|
| 2333 | }
|
| 2334 |
|
| 2335 | return NOERROR;
|
| 2336 | }
|
| 2337 |
|
| 2338 |
|
| 2339 |
|
| 2340 | /* This is a virtual function that returns a media type corresponding with
|
| 2341 | place iPosition in the list. This base class simply returns an error as
|
| 2342 | we support no media types by default but derived classes should override */
|
| 2343 |
|
| 2344 | HRESULT CBasePin::GetMediaType(int iPosition, __inout CMediaType *pMediaType)
|
| 2345 | {
|
| 2346 | UNREFERENCED_PARAMETER(iPosition);
|
| 2347 | UNREFERENCED_PARAMETER(pMediaType);
|
| 2348 | return E_UNEXPECTED;
|
| 2349 | }
|
| 2350 |
|
| 2351 |
|
| 2352 | /* This is a virtual function that returns the current media type version.
|
| 2353 | The base class initialises the media type enumerators with the value 1
|
| 2354 | By default we always returns that same value. A Derived class may change
|
| 2355 | the list of media types available and after doing so it should increment
|
| 2356 | the version either in a method derived from this, or more simply by just
|
| 2357 | incrementing the m_TypeVersion base pin variable. The type enumerators
|
| 2358 | call this when they want to see if their enumerations are out of date */
|
| 2359 |
|
| 2360 | LONG CBasePin::GetMediaTypeVersion()
|
| 2361 | {
|
| 2362 | return m_TypeVersion;
|
| 2363 | }
|
| 2364 |
|
| 2365 |
|
| 2366 | /* Increment the cookie representing the current media type version */
|
| 2367 |
|
| 2368 | void CBasePin::IncrementTypeVersion()
|
| 2369 | {
|
| 2370 | InterlockedIncrement(&m_TypeVersion);
|
| 2371 | }
|
| 2372 |
|
| 2373 |
|
| 2374 | /* Called by IMediaFilter implementation when the state changes from Stopped
|
| 2375 | to either paused or running and in derived classes could do things like
|
| 2376 | commit memory and grab hardware resource (the default is to do nothing) */
|
| 2377 |
|
| 2378 | HRESULT
|
| 2379 | CBasePin::Active(void)
|
| 2380 | {
|
| 2381 | return NOERROR;
|
| 2382 | }
|
| 2383 |
|
| 2384 | /* Called by IMediaFilter implementation when the state changes from
|
| 2385 | to either paused to running and in derived classes could do things like
|
| 2386 | commit memory and grab hardware resource (the default is to do nothing) */
|
| 2387 |
|
| 2388 | HRESULT
|
| 2389 | CBasePin::Run(REFERENCE_TIME tStart)
|
| 2390 | {
|
| 2391 | UNREFERENCED_PARAMETER(tStart);
|
| 2392 | return NOERROR;
|
| 2393 | }
|
| 2394 |
|
| 2395 |
|
| 2396 | /* Also called by the IMediaFilter implementation when the state changes to
|
| 2397 | Stopped at which point you should decommit allocators and free hardware
|
| 2398 | resources you grabbed in the Active call (default is also to do nothing) */
|
| 2399 |
|
| 2400 | HRESULT
|
| 2401 | CBasePin::Inactive(void)
|
| 2402 | {
|
| 2403 | m_bRunTimeError = FALSE;
|
| 2404 | return NOERROR;
|
| 2405 | }
|
| 2406 |
|
| 2407 |
|
| 2408 | // Called when no more data will arrive
|
| 2409 | STDMETHODIMP
|
| 2410 | CBasePin::EndOfStream(void)
|
| 2411 | {
|
| 2412 | return S_OK;
|
| 2413 | }
|
| 2414 |
|
| 2415 |
|
| 2416 | STDMETHODIMP
|
| 2417 | CBasePin::SetSink(IQualityControl * piqc)
|
| 2418 | {
|
| 2419 | CAutoLock cObjectLock(m_pLock);
|
| 2420 | if (piqc) ValidateReadPtr(piqc,sizeof(IQualityControl));
|
| 2421 | m_pQSink = piqc;
|
| 2422 | return NOERROR;
|
| 2423 | } // SetSink
|
| 2424 |
|
| 2425 |
|
| 2426 | STDMETHODIMP
|
| 2427 | CBasePin::Notify(IBaseFilter * pSender, Quality q)
|
| 2428 | {
|
| 2429 | UNREFERENCED_PARAMETER(q);
|
| 2430 | UNREFERENCED_PARAMETER(pSender);
|
| 2431 | DbgBreak("IQualityControl::Notify not over-ridden from CBasePin. (IGNORE is OK)");
|
| 2432 | return E_NOTIMPL;
|
| 2433 | } //Notify
|
| 2434 |
|
| 2435 |
|
| 2436 | // NewSegment notifies of the start/stop/rate applying to the data
|
| 2437 | // about to be received. Default implementation records data and
|
| 2438 | // returns S_OK.
|
| 2439 | // Override this to pass downstream.
|
| 2440 | STDMETHODIMP
|
| 2441 | CBasePin::NewSegment(
|
| 2442 | REFERENCE_TIME tStart,
|
| 2443 | REFERENCE_TIME tStop,
|
| 2444 | double dRate)
|
| 2445 | {
|
| 2446 | m_tStart = tStart;
|
| 2447 | m_tStop = tStop;
|
| 2448 | m_dRate = dRate;
|
| 2449 |
|
| 2450 | return S_OK;
|
| 2451 | }
|
| 2452 |
|
| 2453 |
|
| 2454 | //=====================================================================
|
| 2455 | //=====================================================================
|
| 2456 | // Implements CBaseOutputPin
|
| 2457 | //=====================================================================
|
| 2458 | //=====================================================================
|
| 2459 |
|
| 2460 |
|
| 2461 | CBaseOutputPin::CBaseOutputPin(__in_opt LPCTSTR pObjectName,
|
| 2462 | __in CBaseFilter *pFilter,
|
| 2463 | __in CCritSec *pLock,
|
| 2464 | __inout HRESULT *phr,
|
| 2465 | __in_opt LPCWSTR pName) :
|
| 2466 | CBasePin(pObjectName, pFilter, pLock, phr, pName, PINDIR_OUTPUT),
|
| 2467 | m_pAllocator(NULL),
|
| 2468 | m_pInputPin(NULL)
|
| 2469 | {
|
| 2470 | ASSERT(pFilter);
|
| 2471 | }
|
| 2472 |
|
| 2473 | #ifdef UNICODE
|
| 2474 | CBaseOutputPin::CBaseOutputPin(__in_opt LPCSTR pObjectName,
|
| 2475 | __in CBaseFilter *pFilter,
|
| 2476 | __in CCritSec *pLock,
|
| 2477 | __inout HRESULT *phr,
|
| 2478 | __in_opt LPCWSTR pName) :
|
| 2479 | CBasePin(pObjectName, pFilter, pLock, phr, pName, PINDIR_OUTPUT),
|
| 2480 | m_pAllocator(NULL),
|
| 2481 | m_pInputPin(NULL)
|
| 2482 | {
|
| 2483 | ASSERT(pFilter);
|
| 2484 | }
|
| 2485 | #endif
|
| 2486 |
|
| 2487 | /* This is called after a media type has been proposed
|
| 2488 |
|
| 2489 | Try to complete the connection by agreeing the allocator
|
| 2490 | */
|
| 2491 | HRESULT
|
| 2492 | CBaseOutputPin::CompleteConnect(IPin *pReceivePin)
|
| 2493 | {
|
| 2494 | UNREFERENCED_PARAMETER(pReceivePin);
|
| 2495 | return DecideAllocator(m_pInputPin, &m_pAllocator);
|
| 2496 | }
|
| 2497 |
|
| 2498 |
|
| 2499 | /* This method is called when the output pin is about to try and connect to
|
| 2500 | an input pin. It is at this point that you should try and grab any extra
|
| 2501 | interfaces that you need, in this case IMemInputPin. Because this is
|
| 2502 | only called if we are not currently connected we do NOT need to call
|
| 2503 | BreakConnect. This also makes it easier to derive classes from us as
|
| 2504 | BreakConnect is only called when we actually have to break a connection
|
| 2505 | (or a partly made connection) and not when we are checking a connection */
|
| 2506 |
|
| 2507 | /* Overriden from CBasePin */
|
| 2508 |
|
| 2509 | HRESULT
|
| 2510 | CBaseOutputPin::CheckConnect(IPin * pPin)
|
| 2511 | {
|
| 2512 | HRESULT hr = CBasePin::CheckConnect(pPin);
|
| 2513 | if (FAILED(hr)) {
|
| 2514 | return hr;
|
| 2515 | }
|
| 2516 |
|
| 2517 | // get an input pin and an allocator interface
|
| 2518 | hr = pPin->QueryInterface(IID_IMemInputPin, (void **) &m_pInputPin);
|
| 2519 | if (FAILED(hr)) {
|
| 2520 | return hr;
|
| 2521 | }
|
| 2522 | return NOERROR;
|
| 2523 | }
|
| 2524 |
|
| 2525 |
|
| 2526 | /* Overriden from CBasePin */
|
| 2527 |
|
| 2528 | HRESULT
|
| 2529 | CBaseOutputPin::BreakConnect()
|
| 2530 | {
|
| 2531 | /* Release any allocator we hold */
|
| 2532 |
|
| 2533 | if (m_pAllocator) {
|
| 2534 | // Always decommit the allocator because a downstream filter may or
|
| 2535 | // may not decommit the connection's allocator. A memory leak could
|
| 2536 | // occur if the allocator is not decommited when a connection is broken.
|
| 2537 | HRESULT hr = m_pAllocator->Decommit();
|
| 2538 | if( FAILED( hr ) ) {
|
| 2539 | return hr;
|
| 2540 | }
|
| 2541 |
|
| 2542 | m_pAllocator->Release();
|
| 2543 | m_pAllocator = NULL;
|
| 2544 | }
|
| 2545 |
|
| 2546 | /* Release any input pin interface we hold */
|
| 2547 |
|
| 2548 | if (m_pInputPin) {
|
| 2549 | m_pInputPin->Release();
|
| 2550 | m_pInputPin = NULL;
|
| 2551 | }
|
| 2552 | return NOERROR;
|
| 2553 | }
|
| 2554 |
|
| 2555 |
|
| 2556 | /* This is called when the input pin didn't give us a valid allocator */
|
| 2557 |
|
| 2558 | HRESULT
|
| 2559 | CBaseOutputPin::InitAllocator(__deref_out IMemAllocator **ppAlloc)
|
| 2560 | {
|
| 2561 | return CreateMemoryAllocator(ppAlloc);
|
| 2562 | }
|
| 2563 |
|
| 2564 |
|
| 2565 | /* Decide on an allocator, override this if you want to use your own allocator
|
| 2566 | Override DecideBufferSize to call SetProperties. If the input pin fails
|
| 2567 | the GetAllocator call then this will construct a CMemAllocator and call
|
| 2568 | DecideBufferSize on that, and if that fails then we are completely hosed.
|
| 2569 | If the you succeed the DecideBufferSize call, we will notify the input
|
| 2570 | pin of the selected allocator. NOTE this is called during Connect() which
|
| 2571 | therefore looks after grabbing and locking the object's critical section */
|
| 2572 |
|
| 2573 | // We query the input pin for its requested properties and pass this to
|
| 2574 | // DecideBufferSize to allow it to fulfill requests that it is happy
|
| 2575 | // with (eg most people don't care about alignment and are thus happy to
|
| 2576 | // use the downstream pin's alignment request).
|
| 2577 |
|
| 2578 | HRESULT
|
| 2579 | CBaseOutputPin::DecideAllocator(IMemInputPin *pPin, __deref_out IMemAllocator **ppAlloc)
|
| 2580 | {
|
| 2581 | HRESULT hr = NOERROR;
|
| 2582 | *ppAlloc = NULL;
|
| 2583 |
|
| 2584 | // get downstream prop request
|
| 2585 | // the derived class may modify this in DecideBufferSize, but
|
| 2586 | // we assume that he will consistently modify it the same way,
|
| 2587 | // so we only get it once
|
| 2588 | ALLOCATOR_PROPERTIES prop;
|
| 2589 | ZeroMemory(&prop, sizeof(prop));
|
| 2590 |
|
| 2591 | // whatever he returns, we assume prop is either all zeros
|
| 2592 | // or he has filled it out.
|
| 2593 | pPin->GetAllocatorRequirements(&prop);
|
| 2594 |
|
| 2595 | // if he doesn't care about alignment, then set it to 1
|
| 2596 | if (prop.cbAlign == 0) {
|
| 2597 | prop.cbAlign = 1;
|
| 2598 | }
|
| 2599 |
|
| 2600 | /* Try the allocator provided by the input pin */
|
| 2601 |
|
| 2602 | hr = pPin->GetAllocator(ppAlloc);
|
| 2603 | if (SUCCEEDED(hr)) {
|
| 2604 |
|
| 2605 | hr = DecideBufferSize(*ppAlloc, &prop);
|
| 2606 | if (SUCCEEDED(hr)) {
|
| 2607 | hr = pPin->NotifyAllocator(*ppAlloc, FALSE);
|
| 2608 | if (SUCCEEDED(hr)) {
|
| 2609 | return NOERROR;
|
| 2610 | }
|
| 2611 | }
|
| 2612 | }
|
| 2613 |
|
| 2614 | /* If the GetAllocator failed we may not have an interface */
|
| 2615 |
|
| 2616 | if (*ppAlloc) {
|
| 2617 | (*ppAlloc)->Release();
|
| 2618 | *ppAlloc = NULL;
|
| 2619 | }
|
| 2620 |
|
| 2621 | /* Try the output pin's allocator by the same method */
|
| 2622 |
|
| 2623 | hr = InitAllocator(ppAlloc);
|
| 2624 | if (SUCCEEDED(hr)) {
|
| 2625 |
|
| 2626 | // note - the properties passed here are in the same
|
| 2627 | // structure as above and may have been modified by
|
| 2628 | // the previous call to DecideBufferSize
|
| 2629 | hr = DecideBufferSize(*ppAlloc, &prop);
|
| 2630 | if (SUCCEEDED(hr)) {
|
| 2631 | hr = pPin->NotifyAllocator(*ppAlloc, FALSE);
|
| 2632 | if (SUCCEEDED(hr)) {
|
| 2633 | return NOERROR;
|
| 2634 | }
|
| 2635 | }
|
| 2636 | }
|
| 2637 |
|
| 2638 | /* Likewise we may not have an interface to release */
|
| 2639 |
|
| 2640 | if (*ppAlloc) {
|
| 2641 | (*ppAlloc)->Release();
|
| 2642 | *ppAlloc = NULL;
|
| 2643 | }
|
| 2644 | return hr;
|
| 2645 | }
|
| 2646 |
|
| 2647 |
|
| 2648 | /* This returns an empty sample buffer from the allocator WARNING the same
|
| 2649 | dangers and restrictions apply here as described below for Deliver() */
|
| 2650 |
|
| 2651 | HRESULT
|
| 2652 | CBaseOutputPin::GetDeliveryBuffer(__deref_out IMediaSample ** ppSample,
|
| 2653 | __in_opt REFERENCE_TIME * pStartTime,
|
| 2654 | __in_opt REFERENCE_TIME * pEndTime,
|
| 2655 | DWORD dwFlags)
|
| 2656 | {
|
| 2657 | if (m_pAllocator != NULL) {
|
| 2658 | return m_pAllocator->GetBuffer(ppSample,pStartTime,pEndTime,dwFlags);
|
| 2659 | } else {
|
| 2660 | return E_NOINTERFACE;
|
| 2661 | }
|
| 2662 | }
|
| 2663 |
|
| 2664 |
|
| 2665 | /* Deliver a filled-in sample to the connected input pin. NOTE the object must
|
| 2666 | have locked itself before calling us otherwise we may get halfway through
|
| 2667 | executing this method only to find the filter graph has got in and
|
| 2668 | disconnected us from the input pin. If the filter has no worker threads
|
| 2669 | then the lock is best applied on Receive(), otherwise it should be done
|
| 2670 | when the worker thread is ready to deliver. There is a wee snag to worker
|
| 2671 | threads that this shows up. The worker thread must lock the object when
|
| 2672 | it is ready to deliver a sample, but it may have to wait until a state
|
| 2673 | change has completed, but that may never complete because the state change
|
| 2674 | is waiting for the worker thread to complete. The way to handle this is for
|
| 2675 | the state change code to grab the critical section, then set an abort event
|
| 2676 | for the worker thread, then release the critical section and wait for the
|
| 2677 | worker thread to see the event we set and then signal that it has finished
|
| 2678 | (with another event). At which point the state change code can complete */
|
| 2679 |
|
| 2680 | // note (if you've still got any breath left after reading that) that you
|
| 2681 | // need to release the sample yourself after this call. if the connected
|
| 2682 | // input pin needs to hold onto the sample beyond the call, it will addref
|
| 2683 | // the sample itself.
|
| 2684 |
|
| 2685 | // of course you must release this one and call GetDeliveryBuffer for the
|
| 2686 | // next. You cannot reuse it directly.
|
| 2687 |
|
| 2688 | HRESULT
|
| 2689 | CBaseOutputPin::Deliver(IMediaSample * pSample)
|
| 2690 | {
|
| 2691 | if (m_pInputPin == NULL) {
|
| 2692 | return VFW_E_NOT_CONNECTED;
|
| 2693 | }
|
| 2694 |
|
| 2695 | #ifdef DXMPERF
|
| 2696 | PERFLOG_DELIVER( m_pName ? m_pName : L"CBaseOutputPin", (IPin *) this, (IPin *) m_pInputPin, pSample, &m_mt );
|
| 2697 | #endif // DXMPERF
|
| 2698 |
|
| 2699 | return m_pInputPin->Receive(pSample);
|
| 2700 | }
|
| 2701 |
|
| 2702 |
|
| 2703 | // called from elsewhere in our filter to pass EOS downstream to
|
| 2704 | // our connected input pin
|
| 2705 | HRESULT
|
| 2706 | CBaseOutputPin::DeliverEndOfStream(void)
|
| 2707 | {
|
| 2708 | // remember this is on IPin not IMemInputPin
|
| 2709 | if (m_Connected == NULL) {
|
| 2710 | return VFW_E_NOT_CONNECTED;
|
| 2711 | }
|
| 2712 | return m_Connected->EndOfStream();
|
| 2713 | }
|
| 2714 |
|
| 2715 |
|
| 2716 | /* Commit the allocator's memory, this is called through IMediaFilter
|
| 2717 | which is responsible for locking the object before calling us */
|
| 2718 |
|
| 2719 | HRESULT
|
| 2720 | CBaseOutputPin::Active(void)
|
| 2721 | {
|
| 2722 | if (m_pAllocator == NULL) {
|
| 2723 | return VFW_E_NO_ALLOCATOR;
|
| 2724 | }
|
| 2725 | return m_pAllocator->Commit();
|
| 2726 | }
|
| 2727 |
|
| 2728 |
|
| 2729 | /* Free up or unprepare allocator's memory, this is called through
|
| 2730 | IMediaFilter which is responsible for locking the object first */
|
| 2731 |
|
| 2732 | HRESULT
|
| 2733 | CBaseOutputPin::Inactive(void)
|
| 2734 | {
|
| 2735 | m_bRunTimeError = FALSE;
|
| 2736 | if (m_pAllocator == NULL) {
|
| 2737 | return VFW_E_NO_ALLOCATOR;
|
| 2738 | }
|
| 2739 | return m_pAllocator->Decommit();
|
| 2740 | }
|
| 2741 |
|
| 2742 | // we have a default handling of EndOfStream which is to return
|
| 2743 | // an error, since this should be called on input pins only
|
| 2744 | STDMETHODIMP
|
| 2745 | CBaseOutputPin::EndOfStream(void)
|
| 2746 | {
|
| 2747 | return E_UNEXPECTED;
|
| 2748 | }
|
| 2749 |
|
| 2750 |
|
| 2751 | // BeginFlush should be called on input pins only
|
| 2752 | STDMETHODIMP
|
| 2753 | CBaseOutputPin::BeginFlush(void)
|
| 2754 | {
|
| 2755 | return E_UNEXPECTED;
|
| 2756 | }
|
| 2757 |
|
| 2758 | // EndFlush should be called on input pins only
|
| 2759 | STDMETHODIMP
|
| 2760 | CBaseOutputPin::EndFlush(void)
|
| 2761 | {
|
| 2762 | return E_UNEXPECTED;
|
| 2763 | }
|
| 2764 |
|
| 2765 | // call BeginFlush on the connected input pin
|
| 2766 | HRESULT
|
| 2767 | CBaseOutputPin::DeliverBeginFlush(void)
|
| 2768 | {
|
| 2769 | // remember this is on IPin not IMemInputPin
|
| 2770 | if (m_Connected == NULL) {
|
| 2771 | return VFW_E_NOT_CONNECTED;
|
| 2772 | }
|
| 2773 | return m_Connected->BeginFlush();
|
| 2774 | }
|
| 2775 |
|
| 2776 | // call EndFlush on the connected input pin
|
| 2777 | HRESULT
|
| 2778 | CBaseOutputPin::DeliverEndFlush(void)
|
| 2779 | {
|
| 2780 | // remember this is on IPin not IMemInputPin
|
| 2781 | if (m_Connected == NULL) {
|
| 2782 | return VFW_E_NOT_CONNECTED;
|
| 2783 | }
|
| 2784 | return m_Connected->EndFlush();
|
| 2785 | }
|
| 2786 | // deliver NewSegment to connected pin
|
| 2787 | HRESULT
|
| 2788 | CBaseOutputPin::DeliverNewSegment(
|
| 2789 | REFERENCE_TIME tStart,
|
| 2790 | REFERENCE_TIME tStop,
|
| 2791 | double dRate)
|
| 2792 | {
|
| 2793 | if (m_Connected == NULL) {
|
| 2794 | return VFW_E_NOT_CONNECTED;
|
| 2795 | }
|
| 2796 | return m_Connected->NewSegment(tStart, tStop, dRate);
|
| 2797 | }
|
| 2798 |
|
| 2799 |
|
| 2800 | //=====================================================================
|
| 2801 | //=====================================================================
|
| 2802 | // Implements CBaseInputPin
|
| 2803 | //=====================================================================
|
| 2804 | //=====================================================================
|
| 2805 |
|
| 2806 |
|
| 2807 | /* Constructor creates a default allocator object */
|
| 2808 |
|
| 2809 | CBaseInputPin::CBaseInputPin(__in_opt LPCTSTR pObjectName,
|
| 2810 | __in CBaseFilter *pFilter,
|
| 2811 | __in CCritSec *pLock,
|
| 2812 | __inout HRESULT *phr,
|
| 2813 | __in_opt LPCWSTR pPinName) :
|
| 2814 | CBasePin(pObjectName, pFilter, pLock, phr, pPinName, PINDIR_INPUT),
|
| 2815 | m_pAllocator(NULL),
|
| 2816 | m_bReadOnly(FALSE),
|
| 2817 | m_bFlushing(FALSE)
|
| 2818 | {
|
| 2819 | ZeroMemory(&m_SampleProps, sizeof(m_SampleProps));
|
| 2820 | }
|
| 2821 |
|
| 2822 | #ifdef UNICODE
|
| 2823 | CBaseInputPin::CBaseInputPin(__in LPCSTR pObjectName,
|
| 2824 | __in CBaseFilter *pFilter,
|
| 2825 | __in CCritSec *pLock,
|
| 2826 | __inout HRESULT *phr,
|
| 2827 | __in_opt LPCWSTR pPinName) :
|
| 2828 | CBasePin(pObjectName, pFilter, pLock, phr, pPinName, PINDIR_INPUT),
|
| 2829 | m_pAllocator(NULL),
|
| 2830 | m_bReadOnly(FALSE),
|
| 2831 | m_bFlushing(FALSE)
|
| 2832 | {
|
| 2833 | ZeroMemory(&m_SampleProps, sizeof(m_SampleProps));
|
| 2834 | }
|
| 2835 | #endif
|
| 2836 |
|
| 2837 | /* Destructor releases it's reference count on the default allocator */
|
| 2838 |
|
| 2839 | CBaseInputPin::~CBaseInputPin()
|
| 2840 | {
|
| 2841 | if (m_pAllocator != NULL) {
|
| 2842 | m_pAllocator->Release();
|
| 2843 | m_pAllocator = NULL;
|
| 2844 | }
|
| 2845 | }
|
| 2846 |
|
| 2847 |
|
| 2848 | // override this to publicise our interfaces
|
| 2849 | STDMETHODIMP
|
| 2850 | CBaseInputPin::NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv)
|
| 2851 | {
|
| 2852 | /* Do we know about this interface */
|
| 2853 |
|
| 2854 | if (riid == IID_IMemInputPin) {
|
| 2855 | return GetInterface((IMemInputPin *) this, ppv);
|
| 2856 | } else {
|
| 2857 | return CBasePin::NonDelegatingQueryInterface(riid, ppv);
|
| 2858 | }
|
| 2859 | }
|
| 2860 |
|
| 2861 |
|
| 2862 | /* Return the allocator interface that this input pin would like the output
|
| 2863 | pin to use. NOTE subsequent calls to GetAllocator should all return an
|
| 2864 | interface onto the SAME object so we create one object at the start
|
| 2865 |
|
| 2866 | Note:
|
| 2867 | The allocator is Release()'d on disconnect and replaced on
|
| 2868 | NotifyAllocator().
|
| 2869 |
|
| 2870 | Override this to provide your own allocator.
|
| 2871 | */
|
| 2872 |
|
| 2873 | STDMETHODIMP
|
| 2874 | CBaseInputPin::GetAllocator(
|
| 2875 | __deref_out IMemAllocator **ppAllocator)
|
| 2876 | {
|
| 2877 | CheckPointer(ppAllocator,E_POINTER);
|
| 2878 | ValidateReadWritePtr(ppAllocator,sizeof(IMemAllocator *));
|
| 2879 | CAutoLock cObjectLock(m_pLock);
|
| 2880 |
|
| 2881 | if (m_pAllocator == NULL) {
|
| 2882 | HRESULT hr = CreateMemoryAllocator(&m_pAllocator);
|
| 2883 | if (FAILED(hr)) {
|
| 2884 | return hr;
|
| 2885 | }
|
| 2886 | }
|
| 2887 | ASSERT(m_pAllocator != NULL);
|
| 2888 | *ppAllocator = m_pAllocator;
|
| 2889 | m_pAllocator->AddRef();
|
| 2890 | return NOERROR;
|
| 2891 | }
|
| 2892 |
|
| 2893 |
|
| 2894 | /* Tell the input pin which allocator the output pin is actually going to use
|
| 2895 | Override this if you care - NOTE the locking we do both here and also in
|
| 2896 | GetAllocator is unnecessary but derived classes that do something useful
|
| 2897 | will undoubtedly have to lock the object so this might help remind people */
|
| 2898 |
|
| 2899 | STDMETHODIMP
|
| 2900 | CBaseInputPin::NotifyAllocator(
|
| 2901 | IMemAllocator * pAllocator,
|
| 2902 | BOOL bReadOnly)
|
| 2903 | {
|
| 2904 | CheckPointer(pAllocator,E_POINTER);
|
| 2905 | ValidateReadPtr(pAllocator,sizeof(IMemAllocator));
|
| 2906 | CAutoLock cObjectLock(m_pLock);
|
| 2907 |
|
| 2908 | IMemAllocator *pOldAllocator = m_pAllocator;
|
| 2909 | pAllocator->AddRef();
|
| 2910 | m_pAllocator = pAllocator;
|
| 2911 |
|
| 2912 | if (pOldAllocator != NULL) {
|
| 2913 | pOldAllocator->Release();
|
| 2914 | }
|
| 2915 |
|
| 2916 | // the readonly flag indicates whether samples from this allocator should
|
| 2917 | // be regarded as readonly - if true, then inplace transforms will not be
|
| 2918 | // allowed.
|
| 2919 | m_bReadOnly = (BYTE)bReadOnly;
|
| 2920 | return NOERROR;
|
| 2921 | }
|
| 2922 |
|
| 2923 |
|
| 2924 | HRESULT
|
| 2925 | CBaseInputPin::BreakConnect()
|
| 2926 | {
|
| 2927 | /* We don't need our allocator any more */
|
| 2928 | if (m_pAllocator) {
|
| 2929 | // Always decommit the allocator because a downstream filter may or
|
| 2930 | // may not decommit the connection's allocator. A memory leak could
|
| 2931 | // occur if the allocator is not decommited when a pin is disconnected.
|
| 2932 | HRESULT hr = m_pAllocator->Decommit();
|
| 2933 | if( FAILED( hr ) ) {
|
| 2934 | return hr;
|
| 2935 | }
|
| 2936 |
|
| 2937 | m_pAllocator->Release();
|
| 2938 | m_pAllocator = NULL;
|
| 2939 | }
|
| 2940 |
|
| 2941 | return S_OK;
|
| 2942 | }
|
| 2943 |
|
| 2944 |
|
| 2945 | /* Do something with this media sample - this base class checks to see if the
|
| 2946 | format has changed with this media sample and if so checks that the filter
|
| 2947 | will accept it, generating a run time error if not. Once we have raised a
|
| 2948 | run time error we set a flag so that no more samples will be accepted
|
| 2949 |
|
| 2950 | It is important that any filter should override this method and implement
|
| 2951 | synchronization so that samples are not processed when the pin is
|
| 2952 | disconnected etc
|
| 2953 | */
|
| 2954 |
|
| 2955 | STDMETHODIMP
|
| 2956 | CBaseInputPin::Receive(IMediaSample *pSample)
|
| 2957 | {
|
| 2958 | CheckPointer(pSample,E_POINTER);
|
| 2959 | ValidateReadPtr(pSample,sizeof(IMediaSample));
|
| 2960 | ASSERT(pSample);
|
| 2961 |
|
| 2962 | HRESULT hr = CheckStreaming();
|
| 2963 | if (S_OK != hr) {
|
| 2964 | return hr;
|
| 2965 | }
|
| 2966 |
|
| 2967 | #ifdef DXMPERF
|
| 2968 | PERFLOG_RECEIVE( m_pName ? m_pName : L"CBaseInputPin", (IPin *) m_Connected, (IPin *) this, pSample, &m_mt );
|
| 2969 | #endif // DXMPERF
|
| 2970 |
|
| 2971 |
|
| 2972 | /* Check for IMediaSample2 */
|
| 2973 | IMediaSample2 *pSample2;
|
| 2974 | if (SUCCEEDED(pSample->QueryInterface(IID_IMediaSample2, (void **)&pSample2))) {
|
| 2975 | hr = pSample2->GetProperties(sizeof(m_SampleProps), (PBYTE)&m_SampleProps);
|
| 2976 | pSample2->Release();
|
| 2977 | if (FAILED(hr)) {
|
| 2978 | return hr;
|
| 2979 | }
|
| 2980 | } else {
|
| 2981 | /* Get the properties the hard way */
|
| 2982 | m_SampleProps.cbData = sizeof(m_SampleProps);
|
| 2983 | m_SampleProps.dwTypeSpecificFlags = 0;
|
| 2984 | m_SampleProps.dwStreamId = AM_STREAM_MEDIA;
|
| 2985 | m_SampleProps.dwSampleFlags = 0;
|
| 2986 | if (S_OK == pSample->IsDiscontinuity()) {
|
| 2987 | m_SampleProps.dwSampleFlags |= AM_SAMPLE_DATADISCONTINUITY;
|
| 2988 | }
|
| 2989 | if (S_OK == pSample->IsPreroll()) {
|
| 2990 | m_SampleProps.dwSampleFlags |= AM_SAMPLE_PREROLL;
|
| 2991 | }
|
| 2992 | if (S_OK == pSample->IsSyncPoint()) {
|
| 2993 | m_SampleProps.dwSampleFlags |= AM_SAMPLE_SPLICEPOINT;
|
| 2994 | }
|
| 2995 | if (SUCCEEDED(pSample->GetTime(&m_SampleProps.tStart,
|
| 2996 | &m_SampleProps.tStop))) {
|
| 2997 | m_SampleProps.dwSampleFlags |= AM_SAMPLE_TIMEVALID |
|
| 2998 | AM_SAMPLE_STOPVALID;
|
| 2999 | }
|
| 3000 | if (S_OK == pSample->GetMediaType(&m_SampleProps.pMediaType)) {
|
| 3001 | m_SampleProps.dwSampleFlags |= AM_SAMPLE_TYPECHANGED;
|
| 3002 | }
|
| 3003 | pSample->GetPointer(&m_SampleProps.pbBuffer);
|
| 3004 | m_SampleProps.lActual = pSample->GetActualDataLength();
|
| 3005 | m_SampleProps.cbBuffer = pSample->GetSize();
|
| 3006 | }
|
| 3007 |
|
| 3008 | /* Has the format changed in this sample */
|
| 3009 |
|
| 3010 | if (!(m_SampleProps.dwSampleFlags & AM_SAMPLE_TYPECHANGED)) {
|
| 3011 | return NOERROR;
|
| 3012 | }
|
| 3013 |
|
| 3014 | /* Check the derived class accepts this format */
|
| 3015 | /* This shouldn't fail as the source must call QueryAccept first */
|
| 3016 |
|
| 3017 | hr = CheckMediaType((CMediaType *)m_SampleProps.pMediaType);
|
| 3018 |
|
| 3019 | if (hr == NOERROR) {
|
| 3020 | return NOERROR;
|
| 3021 | }
|
| 3022 |
|
| 3023 | /* Raise a runtime error if we fail the media type */
|
| 3024 |
|
| 3025 | m_bRunTimeError = TRUE;
|
| 3026 | EndOfStream();
|
| 3027 | m_pFilter->NotifyEvent(EC_ERRORABORT,VFW_E_TYPE_NOT_ACCEPTED,0);
|
| 3028 | return VFW_E_INVALIDMEDIATYPE;
|
| 3029 | }
|
| 3030 |
|
| 3031 |
|
| 3032 | /* Receive multiple samples */
|
| 3033 | STDMETHODIMP
|
| 3034 | CBaseInputPin::ReceiveMultiple (
|
| 3035 | __in_ecount(nSamples) IMediaSample **pSamples,
|
| 3036 | long nSamples,
|
| 3037 | __out long *nSamplesProcessed)
|
| 3038 | {
|
| 3039 | CheckPointer(pSamples,E_POINTER);
|
| 3040 | ValidateReadPtr(pSamples,nSamples * sizeof(IMediaSample *));
|
| 3041 |
|
| 3042 | HRESULT hr = S_OK;
|
| 3043 | *nSamplesProcessed = 0;
|
| 3044 | while (nSamples-- > 0) {
|
| 3045 | hr = Receive(pSamples[*nSamplesProcessed]);
|
| 3046 |
|
| 3047 | /* S_FALSE means don't send any more */
|
| 3048 | if (hr != S_OK) {
|
| 3049 | break;
|
| 3050 | }
|
| 3051 | (*nSamplesProcessed)++;
|
| 3052 | }
|
| 3053 | return hr;
|
| 3054 | }
|
| 3055 |
|
| 3056 | /* See if Receive() might block */
|
| 3057 | STDMETHODIMP
|
| 3058 | CBaseInputPin::ReceiveCanBlock()
|
| 3059 | {
|
| 3060 | /* Ask all the output pins if they block
|
| 3061 | If there are no output pin assume we do block
|
| 3062 | */
|
| 3063 | int cPins = m_pFilter->GetPinCount();
|
| 3064 | int cOutputPins = 0;
|
| 3065 | for (int c = 0; c < cPins; c++) {
|
| 3066 | CBasePin *pPin = m_pFilter->GetPin(c);
|
| 3067 | if (NULL == pPin) {
|
| 3068 | break;
|
| 3069 | }
|
| 3070 | PIN_DIRECTION pd;
|
| 3071 | HRESULT hr = pPin->QueryDirection(&pd);
|
| 3072 | if (FAILED(hr)) {
|
| 3073 | return hr;
|
| 3074 | }
|
| 3075 |
|
| 3076 | if (pd == PINDIR_OUTPUT) {
|
| 3077 |
|
| 3078 | IPin *pConnected;
|
| 3079 | hr = pPin->ConnectedTo(&pConnected);
|
| 3080 | if (SUCCEEDED(hr)) {
|
| 3081 | ASSERT(pConnected != NULL);
|
| 3082 | cOutputPins++;
|
| 3083 | IMemInputPin *pInputPin;
|
| 3084 | hr = pConnected->QueryInterface(
|
| 3085 | IID_IMemInputPin,
|
| 3086 | (void **)&pInputPin);
|
| 3087 | pConnected->Release();
|
| 3088 | if (SUCCEEDED(hr)) {
|
| 3089 | hr = pInputPin->ReceiveCanBlock();
|
| 3090 | pInputPin->Release();
|
| 3091 | if (hr != S_FALSE) {
|
| 3092 | return S_OK;
|
| 3093 | }
|
| 3094 | } else {
|
| 3095 | /* There's a transport we don't understand here */
|
| 3096 | return S_OK;
|
| 3097 | }
|
| 3098 | }
|
| 3099 | }
|
| 3100 | }
|
| 3101 | return cOutputPins == 0 ? S_OK : S_FALSE;
|
| 3102 | }
|
| 3103 |
|
| 3104 | // Default handling for BeginFlush - call at the beginning
|
| 3105 | // of your implementation (makes sure that all Receive calls
|
| 3106 | // fail). After calling this, you need to free any queued data
|
| 3107 | // and then call downstream.
|
| 3108 | STDMETHODIMP
|
| 3109 | CBaseInputPin::BeginFlush(void)
|
| 3110 | {
|
| 3111 | // BeginFlush is NOT synchronized with streaming but is part of
|
| 3112 | // a control action - hence we synchronize with the filter
|
| 3113 | CAutoLock lck(m_pLock);
|
| 3114 |
|
| 3115 | // if we are already in mid-flush, this is probably a mistake
|
| 3116 | // though not harmful - try to pick it up for now so I can think about it
|
| 3117 | ASSERT(!m_bFlushing);
|
| 3118 |
|
| 3119 | // first thing to do is ensure that no further Receive calls succeed
|
| 3120 | m_bFlushing = TRUE;
|
| 3121 |
|
| 3122 | // now discard any data and call downstream - must do that
|
| 3123 | // in derived classes
|
| 3124 | return S_OK;
|
| 3125 | }
|
| 3126 |
|
| 3127 | // default handling for EndFlush - call at end of your implementation
|
| 3128 | // - before calling this, ensure that there is no queued data and no thread
|
| 3129 | // pushing any more without a further receive, then call downstream,
|
| 3130 | // then call this method to clear the m_bFlushing flag and re-enable
|
| 3131 | // receives
|
| 3132 | STDMETHODIMP
|
| 3133 | CBaseInputPin::EndFlush(void)
|
| 3134 | {
|
| 3135 | // Endlush is NOT synchronized with streaming but is part of
|
| 3136 | // a control action - hence we synchronize with the filter
|
| 3137 | CAutoLock lck(m_pLock);
|
| 3138 |
|
| 3139 | // almost certainly a mistake if we are not in mid-flush
|
| 3140 | ASSERT(m_bFlushing);
|
| 3141 |
|
| 3142 | // before calling, sync with pushing thread and ensure
|
| 3143 | // no more data is going downstream, then call EndFlush on
|
| 3144 | // downstream pins.
|
| 3145 |
|
| 3146 | // now re-enable Receives
|
| 3147 | m_bFlushing = FALSE;
|
| 3148 |
|
| 3149 | // No more errors
|
| 3150 | m_bRunTimeError = FALSE;
|
| 3151 |
|
| 3152 | return S_OK;
|
| 3153 | }
|
| 3154 |
|
| 3155 |
|
| 3156 | STDMETHODIMP
|
| 3157 | CBaseInputPin::Notify(IBaseFilter * pSender, Quality q)
|
| 3158 | {
|
| 3159 | UNREFERENCED_PARAMETER(q);
|
| 3160 | CheckPointer(pSender,E_POINTER);
|
| 3161 | ValidateReadPtr(pSender,sizeof(IBaseFilter));
|
| 3162 | DbgBreak("IQuality::Notify called on an input pin");
|
| 3163 | return NOERROR;
|
| 3164 | } // Notify
|
| 3165 |
|
| 3166 | /* Free up or unprepare allocator's memory, this is called through
|
| 3167 | IMediaFilter which is responsible for locking the object first */
|
| 3168 |
|
| 3169 | HRESULT
|
| 3170 | CBaseInputPin::Inactive(void)
|
| 3171 | {
|
| 3172 | m_bRunTimeError = FALSE;
|
| 3173 | if (m_pAllocator == NULL) {
|
| 3174 | return VFW_E_NO_ALLOCATOR;
|
| 3175 | }
|
| 3176 |
|
| 3177 | m_bFlushing = FALSE;
|
| 3178 |
|
| 3179 | return m_pAllocator->Decommit();
|
| 3180 | }
|
| 3181 |
|
| 3182 | // what requirements do we have of the allocator - override if you want
|
| 3183 | // to support other people's allocators but need a specific alignment
|
| 3184 | // or prefix.
|
| 3185 | STDMETHODIMP
|
| 3186 | CBaseInputPin::GetAllocatorRequirements(__out ALLOCATOR_PROPERTIES*pProps)
|
| 3187 | {
|
| 3188 | UNREFERENCED_PARAMETER(pProps);
|
| 3189 | return E_NOTIMPL;
|
| 3190 | }
|
| 3191 |
|
| 3192 | // Check if it's OK to process data
|
| 3193 | //
|
| 3194 | HRESULT
|
| 3195 | CBaseInputPin::CheckStreaming()
|
| 3196 | {
|
| 3197 | // Shouldn't be able to get any data if we're not connected!
|
| 3198 | ASSERT(IsConnected());
|
| 3199 |
|
| 3200 | // Don't process stuff in Stopped state
|
| 3201 | if (IsStopped()) {
|
| 3202 | return VFW_E_WRONG_STATE;
|
| 3203 | }
|
| 3204 | if (m_bFlushing) {
|
| 3205 | return S_FALSE;
|
| 3206 | }
|
| 3207 | if (m_bRunTimeError) {
|
| 3208 | return VFW_E_RUNTIME_ERROR;
|
| 3209 | }
|
| 3210 | return S_OK;
|
| 3211 | }
|
| 3212 |
|
| 3213 | // Pass on the Quality notification q to
|
| 3214 | // a. Our QualityControl sink (if we have one) or else
|
| 3215 | // b. to our upstream filter
|
| 3216 | // and if that doesn't work, throw it away with a bad return code
|
| 3217 | HRESULT
|
| 3218 | CBaseInputPin::PassNotify(Quality& q)
|
| 3219 | {
|
| 3220 | // We pass the message on, which means that we find the quality sink
|
| 3221 | // for our input pin and send it there
|
| 3222 |
|
| 3223 | DbgLog((LOG_TRACE,3,TEXT("Passing Quality notification through transform")));
|
| 3224 | if (m_pQSink!=NULL) {
|
| 3225 | return m_pQSink->Notify(m_pFilter, q);
|
| 3226 | } else {
|
| 3227 | // no sink set, so pass it upstream
|
| 3228 | HRESULT hr;
|
| 3229 | IQualityControl * pIQC;
|
| 3230 |
|
| 3231 | hr = VFW_E_NOT_FOUND; // default
|
| 3232 | if (m_Connected) {
|
| 3233 | m_Connected->QueryInterface(IID_IQualityControl, (void**)&pIQC);
|
| 3234 |
|
| 3235 | if (pIQC!=NULL) {
|
| 3236 | hr = pIQC->Notify(m_pFilter, q);
|
| 3237 | pIQC->Release();
|
| 3238 | }
|
| 3239 | }
|
| 3240 | return hr;
|
| 3241 | }
|
| 3242 |
|
| 3243 | } // PassNotify
|
| 3244 |
|
| 3245 | //=====================================================================
|
| 3246 | //=====================================================================
|
| 3247 | // Memory allocation class, implements CMediaSample
|
| 3248 | //=====================================================================
|
| 3249 | //=====================================================================
|
| 3250 |
|
| 3251 |
|
| 3252 | /* NOTE The implementation of this class calls the CUnknown constructor with
|
| 3253 | a NULL outer unknown pointer. This has the effect of making us a self
|
| 3254 | contained class, ie any QueryInterface, AddRef or Release calls will be
|
| 3255 | routed to the class's NonDelegatingUnknown methods. You will typically
|
| 3256 | find that the classes that do this then override one or more of these
|
| 3257 | virtual functions to provide more specialised behaviour. A good example
|
| 3258 | of this is where a class wants to keep the QueryInterface internal but
|
| 3259 | still wants it's lifetime controlled by the external object */
|
| 3260 |
|
| 3261 | /* The last two parameters have default values of NULL and zero */
|
| 3262 |
|
| 3263 | CMediaSample::CMediaSample(__in_opt LPCTSTR pName,
|
| 3264 | __in_opt CBaseAllocator *pAllocator,
|
| 3265 | __inout_opt HRESULT *phr,
|
| 3266 | __in_bcount_opt(length) LPBYTE pBuffer,
|
| 3267 | LONG length) :
|
| 3268 | m_pBuffer(pBuffer), // Initialise the buffer
|
| 3269 | m_cbBuffer(length), // And it's length
|
| 3270 | m_lActual(length), // By default, actual = length
|
| 3271 | m_pMediaType(NULL), // No media type change
|
| 3272 | m_dwFlags(0), // Nothing set
|
| 3273 | m_cRef(0), // 0 ref count
|
| 3274 | m_dwTypeSpecificFlags(0), // Type specific flags
|
| 3275 | m_dwStreamId(AM_STREAM_MEDIA), // Stream id
|
| 3276 | m_pAllocator(pAllocator) // Allocator
|
| 3277 | {
|
| 3278 | #ifdef DXMPERF
|
| 3279 | PERFLOG_CTOR( pName ? pName : L"CMediaSample", (IMediaSample *) this );
|
| 3280 | #endif // DXMPERF
|
| 3281 |
|
| 3282 | /* We must have an owner and it must also be derived from class
|
| 3283 | CBaseAllocator BUT we do not hold a reference count on it */
|
| 3284 |
|
| 3285 | ASSERT(pAllocator);
|
| 3286 |
|
| 3287 | if (length < 0) {
|
| 3288 | *phr = VFW_E_BUFFER_OVERFLOW;
|
| 3289 | m_cbBuffer = 0;
|
| 3290 | }
|
| 3291 | }
|
| 3292 |
|
| 3293 | #ifdef UNICODE
|
| 3294 | CMediaSample::CMediaSample(__in_opt LPCSTR pName,
|
| 3295 | __in_opt CBaseAllocator *pAllocator,
|
| 3296 | __inout_opt HRESULT *phr,
|
| 3297 | __in_bcount_opt(length) LPBYTE pBuffer,
|
| 3298 | LONG length) :
|
| 3299 | m_pBuffer(pBuffer), // Initialise the buffer
|
| 3300 | m_cbBuffer(length), // And it's length
|
| 3301 | m_lActual(length), // By default, actual = length
|
| 3302 | m_pMediaType(NULL), // No media type change
|
| 3303 | m_dwFlags(0), // Nothing set
|
| 3304 | m_cRef(0), // 0 ref count
|
| 3305 | m_dwTypeSpecificFlags(0), // Type specific flags
|
| 3306 | m_dwStreamId(AM_STREAM_MEDIA), // Stream id
|
| 3307 | m_pAllocator(pAllocator) // Allocator
|
| 3308 | {
|
| 3309 | #ifdef DXMPERF
|
| 3310 | PERFLOG_CTOR( L"CMediaSample", (IMediaSample *) this );
|
| 3311 | #endif // DXMPERF
|
| 3312 |
|
| 3313 | /* We must have an owner and it must also be derived from class
|
| 3314 | CBaseAllocator BUT we do not hold a reference count on it */
|
| 3315 |
|
| 3316 | ASSERT(pAllocator);
|
| 3317 | }
|
| 3318 | #endif
|
| 3319 |
|
| 3320 | /* Destructor deletes the media type memory */
|
| 3321 |
|
| 3322 | CMediaSample::~CMediaSample()
|
| 3323 | {
|
| 3324 | #ifdef DXMPERF
|
| 3325 | PERFLOG_DTOR( L"CMediaSample", (IMediaSample *) this );
|
| 3326 | #endif // DXMPERF
|
| 3327 |
|
| 3328 | if (m_pMediaType) {
|
| 3329 | DeleteMediaType(m_pMediaType);
|
| 3330 | }
|
| 3331 | }
|
| 3332 |
|
| 3333 | /* Override this to publicise our interfaces */
|
| 3334 |
|
| 3335 | STDMETHODIMP
|
| 3336 | CMediaSample::QueryInterface(REFIID riid, __deref_out void **ppv)
|
| 3337 | {
|
| 3338 | if (riid == IID_IMediaSample ||
|
| 3339 | riid == IID_IMediaSample2 ||
|
| 3340 | riid == IID_IUnknown) {
|
| 3341 | return GetInterface((IMediaSample *) this, ppv);
|
| 3342 | } else {
|
| 3343 | *ppv = NULL;
|
| 3344 | return E_NOINTERFACE;
|
| 3345 | }
|
| 3346 | }
|
| 3347 |
|
| 3348 | STDMETHODIMP_(ULONG)
|
| 3349 | CMediaSample::AddRef()
|
| 3350 | {
|
| 3351 | return InterlockedIncrement(&m_cRef);
|
| 3352 | }
|
| 3353 |
|
| 3354 |
|
| 3355 | // -- CMediaSample lifetimes --
|
| 3356 | //
|
| 3357 | // On final release of this sample buffer it is not deleted but
|
| 3358 | // returned to the freelist of the owning memory allocator
|
| 3359 | //
|
| 3360 | // The allocator may be waiting for the last buffer to be placed on the free
|
| 3361 | // list in order to decommit all the memory, so the ReleaseBuffer() call may
|
| 3362 | // result in this sample being deleted. We also need to hold a refcount on
|
| 3363 | // the allocator to stop that going away until we have finished with this.
|
| 3364 | // However, we cannot release the allocator before the ReleaseBuffer, as the
|
| 3365 | // release may cause us to be deleted. Similarly we can't do it afterwards.
|
| 3366 | //
|
| 3367 | // Thus we must leave it to the allocator to hold an addref on our behalf.
|
| 3368 | // When he issues us in GetBuffer, he addref's himself. When ReleaseBuffer
|
| 3369 | // is called, he releases himself, possibly causing us and him to be deleted.
|
| 3370 |
|
| 3371 |
|
| 3372 | STDMETHODIMP_(ULONG)
|
| 3373 | CMediaSample::Release()
|
| 3374 | {
|
| 3375 | /* Decrement our own private reference count */
|
| 3376 | LONG lRef;
|
| 3377 | if (m_cRef == 1) {
|
| 3378 | lRef = 0;
|
| 3379 | m_cRef = 0;
|
| 3380 | } else {
|
| 3381 | lRef = InterlockedDecrement(&m_cRef);
|
| 3382 | }
|
| 3383 | ASSERT(lRef >= 0);
|
| 3384 |
|
| 3385 | DbgLog((LOG_MEMORY,3,TEXT(" Unknown %X ref-- = %d"),
|
| 3386 | this, m_cRef));
|
| 3387 |
|
| 3388 | /* Did we release our final reference count */
|
| 3389 | if (lRef == 0) {
|
| 3390 | /* Free all resources */
|
| 3391 | if (m_dwFlags & Sample_TypeChanged) {
|
| 3392 | SetMediaType(NULL);
|
| 3393 | }
|
| 3394 | ASSERT(m_pMediaType == NULL);
|
| 3395 | m_dwFlags = 0;
|
| 3396 | m_dwTypeSpecificFlags = 0;
|
| 3397 | m_dwStreamId = AM_STREAM_MEDIA;
|
| 3398 |
|
| 3399 | /* This may cause us to be deleted */
|
| 3400 | // Our refcount is reliably 0 thus no-one will mess with us
|
| 3401 | m_pAllocator->ReleaseBuffer(this);
|
| 3402 | }
|
| 3403 | return (ULONG)lRef;
|
| 3404 | }
|
| 3405 |
|
| 3406 |
|
| 3407 | // set the buffer pointer and length. Used by allocators that
|
| 3408 | // want variable sized pointers or pointers into already-read data.
|
| 3409 | // This is only available through a CMediaSample* not an IMediaSample*
|
| 3410 | // and so cannot be changed by clients.
|
| 3411 | HRESULT
|
| 3412 | CMediaSample::SetPointer(__in_bcount(cBytes) BYTE * ptr, LONG cBytes)
|
| 3413 | {
|
| 3414 | if (cBytes < 0) {
|
| 3415 | return VFW_E_BUFFER_OVERFLOW;
|
| 3416 | }
|
| 3417 | m_pBuffer = ptr; // new buffer area (could be null)
|
| 3418 | m_cbBuffer = cBytes; // length of buffer
|
| 3419 | m_lActual = cBytes; // length of data in buffer (assume full)
|
| 3420 |
|
| 3421 | return S_OK;
|
| 3422 | }
|
| 3423 |
|
| 3424 |
|
| 3425 | // get me a read/write pointer to this buffer's memory. I will actually
|
| 3426 | // want to use sizeUsed bytes.
|
| 3427 | STDMETHODIMP
|
| 3428 | CMediaSample::GetPointer(__deref_out BYTE ** ppBuffer)
|
| 3429 | {
|
| 3430 | ValidateReadWritePtr(ppBuffer,sizeof(BYTE *));
|
| 3431 |
|
| 3432 | // creator must have set pointer either during
|
| 3433 | // constructor or by SetPointer
|
| 3434 | ASSERT(m_pBuffer);
|
| 3435 |
|
| 3436 | *ppBuffer = m_pBuffer;
|
| 3437 | return NOERROR;
|
| 3438 | }
|
| 3439 |
|
| 3440 |
|
| 3441 | // return the size in bytes of this buffer
|
| 3442 | STDMETHODIMP_(LONG)
|
| 3443 | CMediaSample::GetSize(void)
|
| 3444 | {
|
| 3445 | return m_cbBuffer;
|
| 3446 | }
|
| 3447 |
|
| 3448 |
|
| 3449 | // get the stream time at which this sample should start and finish.
|
| 3450 | STDMETHODIMP
|
| 3451 | CMediaSample::GetTime(
|
| 3452 | __out REFERENCE_TIME * pTimeStart, // put time here
|
| 3453 | __out REFERENCE_TIME * pTimeEnd
|
| 3454 | )
|
| 3455 | {
|
| 3456 | ValidateReadWritePtr(pTimeStart,sizeof(REFERENCE_TIME));
|
| 3457 | ValidateReadWritePtr(pTimeEnd,sizeof(REFERENCE_TIME));
|
| 3458 |
|
| 3459 | if (!(m_dwFlags & Sample_StopValid)) {
|
| 3460 | if (!(m_dwFlags & Sample_TimeValid)) {
|
| 3461 | return VFW_E_SAMPLE_TIME_NOT_SET;
|
| 3462 | } else {
|
| 3463 | *pTimeStart = m_Start;
|
| 3464 |
|
| 3465 | // Make sure old stuff works
|
| 3466 | *pTimeEnd = m_Start + 1;
|
| 3467 | return VFW_S_NO_STOP_TIME;
|
| 3468 | }
|
| 3469 | }
|
| 3470 |
|
| 3471 | *pTimeStart = m_Start;
|
| 3472 | *pTimeEnd = m_End;
|
| 3473 | return NOERROR;
|
| 3474 | }
|
| 3475 |
|
| 3476 |
|
| 3477 | // Set the stream time at which this sample should start and finish.
|
| 3478 | // NULL pointers means the time is reset
|
| 3479 | STDMETHODIMP
|
| 3480 | CMediaSample::SetTime(
|
| 3481 | __in_opt REFERENCE_TIME * pTimeStart,
|
| 3482 | __in_opt REFERENCE_TIME * pTimeEnd
|
| 3483 | )
|
| 3484 | {
|
| 3485 | if (pTimeStart == NULL) {
|
| 3486 | ASSERT(pTimeEnd == NULL);
|
| 3487 | m_dwFlags &= ~(Sample_TimeValid | Sample_StopValid);
|
| 3488 | } else {
|
| 3489 | if (pTimeEnd == NULL) {
|
| 3490 | m_Start = *pTimeStart;
|
| 3491 | m_dwFlags |= Sample_TimeValid;
|
| 3492 | m_dwFlags &= ~Sample_StopValid;
|
| 3493 | } else {
|
| 3494 | ValidateReadPtr(pTimeStart,sizeof(REFERENCE_TIME));
|
| 3495 | ValidateReadPtr(pTimeEnd,sizeof(REFERENCE_TIME));
|
| 3496 | ASSERT(*pTimeEnd >= *pTimeStart);
|
| 3497 |
|
| 3498 | m_Start = *pTimeStart;
|
| 3499 | m_End = *pTimeEnd;
|
| 3500 | m_dwFlags |= Sample_TimeValid | Sample_StopValid;
|
| 3501 | }
|
| 3502 | }
|
| 3503 | return NOERROR;
|
| 3504 | }
|
| 3505 |
|
| 3506 |
|
| 3507 | // get the media times (eg bytes) for this sample
|
| 3508 | STDMETHODIMP
|
| 3509 | CMediaSample::GetMediaTime(
|
| 3510 | __out LONGLONG * pTimeStart,
|
| 3511 | __out LONGLONG * pTimeEnd
|
| 3512 | )
|
| 3513 | {
|
| 3514 | ValidateReadWritePtr(pTimeStart,sizeof(LONGLONG));
|
| 3515 | ValidateReadWritePtr(pTimeEnd,sizeof(LONGLONG));
|
| 3516 |
|
| 3517 | if (!(m_dwFlags & Sample_MediaTimeValid)) {
|
| 3518 | return VFW_E_MEDIA_TIME_NOT_SET;
|
| 3519 | }
|
| 3520 |
|
| 3521 | *pTimeStart = m_MediaStart;
|
| 3522 | *pTimeEnd = (m_MediaStart + m_MediaEnd);
|
| 3523 | return NOERROR;
|
| 3524 | }
|
| 3525 |
|
| 3526 |
|
| 3527 | // Set the media times for this sample
|
| 3528 | STDMETHODIMP
|
| 3529 | CMediaSample::SetMediaTime(
|
| 3530 | __in_opt LONGLONG * pTimeStart,
|
| 3531 | __in_opt LONGLONG * pTimeEnd
|
| 3532 | )
|
| 3533 | {
|
| 3534 | if (pTimeStart == NULL) {
|
| 3535 | ASSERT(pTimeEnd == NULL);
|
| 3536 | m_dwFlags &= ~Sample_MediaTimeValid;
|
| 3537 | } else {
|
| 3538 | if (NULL == pTimeEnd) {
|
| 3539 | return E_POINTER;
|
| 3540 | }
|
| 3541 | ValidateReadPtr(pTimeStart,sizeof(LONGLONG));
|
| 3542 | ValidateReadPtr(pTimeEnd,sizeof(LONGLONG));
|
| 3543 | ASSERT(*pTimeEnd >= *pTimeStart);
|
| 3544 |
|
| 3545 | m_MediaStart = *pTimeStart;
|
| 3546 | m_MediaEnd = (LONG)(*pTimeEnd - *pTimeStart);
|
| 3547 | m_dwFlags |= Sample_MediaTimeValid;
|
| 3548 | }
|
| 3549 | return NOERROR;
|
| 3550 | }
|
| 3551 |
|
| 3552 |
|
| 3553 | STDMETHODIMP
|
| 3554 | CMediaSample::IsSyncPoint(void)
|
| 3555 | {
|
| 3556 | if (m_dwFlags & Sample_SyncPoint) {
|
| 3557 | return S_OK;
|
| 3558 | } else {
|
| 3559 | return S_FALSE;
|
| 3560 | }
|
| 3561 | }
|
| 3562 |
|
| 3563 |
|
| 3564 | STDMETHODIMP
|
| 3565 | CMediaSample::SetSyncPoint(BOOL bIsSyncPoint)
|
| 3566 | {
|
| 3567 | if (bIsSyncPoint) {
|
| 3568 | m_dwFlags |= Sample_SyncPoint;
|
| 3569 | } else {
|
| 3570 | m_dwFlags &= ~Sample_SyncPoint;
|
| 3571 | }
|
| 3572 | return NOERROR;
|
| 3573 | }
|
| 3574 |
|
| 3575 | // returns S_OK if there is a discontinuity in the data (this same is
|
| 3576 | // not a continuation of the previous stream of data
|
| 3577 | // - there has been a seek).
|
| 3578 | STDMETHODIMP
|
| 3579 | CMediaSample::IsDiscontinuity(void)
|
| 3580 | {
|
| 3581 | if (m_dwFlags & Sample_Discontinuity) {
|
| 3582 | return S_OK;
|
| 3583 | } else {
|
| 3584 | return S_FALSE;
|
| 3585 | }
|
| 3586 | }
|
| 3587 |
|
| 3588 | // set the discontinuity property - TRUE if this sample is not a
|
| 3589 | // continuation, but a new sample after a seek.
|
| 3590 | STDMETHODIMP
|
| 3591 | CMediaSample::SetDiscontinuity(BOOL bDiscont)
|
| 3592 | {
|
| 3593 | // should be TRUE or FALSE
|
| 3594 | if (bDiscont) {
|
| 3595 | m_dwFlags |= Sample_Discontinuity;
|
| 3596 | } else {
|
| 3597 | m_dwFlags &= ~Sample_Discontinuity;
|
| 3598 | }
|
| 3599 | return S_OK;
|
| 3600 | }
|
| 3601 |
|
| 3602 | STDMETHODIMP
|
| 3603 | CMediaSample::IsPreroll(void)
|
| 3604 | {
|
| 3605 | if (m_dwFlags & Sample_Preroll) {
|
| 3606 | return S_OK;
|
| 3607 | } else {
|
| 3608 | return S_FALSE;
|
| 3609 | }
|
| 3610 | }
|
| 3611 |
|
| 3612 |
|
| 3613 | STDMETHODIMP
|
| 3614 | CMediaSample::SetPreroll(BOOL bIsPreroll)
|
| 3615 | {
|
| 3616 | if (bIsPreroll) {
|
| 3617 | m_dwFlags |= Sample_Preroll;
|
| 3618 | } else {
|
| 3619 | m_dwFlags &= ~Sample_Preroll;
|
| 3620 | }
|
| 3621 | return NOERROR;
|
| 3622 | }
|
| 3623 |
|
| 3624 | STDMETHODIMP_(LONG)
|
| 3625 | CMediaSample::GetActualDataLength(void)
|
| 3626 | {
|
| 3627 | return m_lActual;
|
| 3628 | }
|
| 3629 |
|
| 3630 |
|
| 3631 | STDMETHODIMP
|
| 3632 | CMediaSample::SetActualDataLength(LONG lActual)
|
| 3633 | {
|
| 3634 | if (lActual > m_cbBuffer || lActual < 0) {
|
| 3635 | ASSERT(lActual <= GetSize());
|
| 3636 | return VFW_E_BUFFER_OVERFLOW;
|
| 3637 | }
|
| 3638 | m_lActual = lActual;
|
| 3639 | return NOERROR;
|
| 3640 | }
|
| 3641 |
|
| 3642 |
|
| 3643 | /* These allow for limited format changes in band */
|
| 3644 |
|
| 3645 | STDMETHODIMP
|
| 3646 | CMediaSample::GetMediaType(__deref_out AM_MEDIA_TYPE **ppMediaType)
|
| 3647 | {
|
| 3648 | ValidateReadWritePtr(ppMediaType,sizeof(AM_MEDIA_TYPE *));
|
| 3649 | ASSERT(ppMediaType);
|
| 3650 |
|
| 3651 | /* Do we have a new media type for them */
|
| 3652 |
|
| 3653 | if (!(m_dwFlags & Sample_TypeChanged)) {
|
| 3654 | ASSERT(m_pMediaType == NULL);
|
| 3655 | *ppMediaType = NULL;
|
| 3656 | return S_FALSE;
|
| 3657 | }
|
| 3658 |
|
| 3659 | ASSERT(m_pMediaType);
|
| 3660 |
|
| 3661 | /* Create a copy of our media type */
|
| 3662 |
|
| 3663 | *ppMediaType = CreateMediaType(m_pMediaType);
|
| 3664 | if (*ppMediaType == NULL) {
|
| 3665 | return E_OUTOFMEMORY;
|
| 3666 | }
|
| 3667 | return NOERROR;
|
| 3668 | }
|
| 3669 |
|
| 3670 |
|
| 3671 | /* Mark this sample as having a different format type */
|
| 3672 |
|
| 3673 | STDMETHODIMP
|
| 3674 | CMediaSample::SetMediaType(__in_opt AM_MEDIA_TYPE *pMediaType)
|
| 3675 | {
|
| 3676 | /* Delete the current media type */
|
| 3677 |
|
| 3678 | if (m_pMediaType) {
|
| 3679 | DeleteMediaType(m_pMediaType);
|
| 3680 | m_pMediaType = NULL;
|
| 3681 | }
|
| 3682 |
|
| 3683 | /* Mechanism for resetting the format type */
|
| 3684 |
|
| 3685 | if (pMediaType == NULL) {
|
| 3686 | m_dwFlags &= ~Sample_TypeChanged;
|
| 3687 | return NOERROR;
|
| 3688 | }
|
| 3689 |
|
| 3690 | ASSERT(pMediaType);
|
| 3691 | ValidateReadPtr(pMediaType,sizeof(AM_MEDIA_TYPE));
|
| 3692 |
|
| 3693 | /* Take a copy of the media type */
|
| 3694 |
|
| 3695 | m_pMediaType = CreateMediaType(pMediaType);
|
| 3696 | if (m_pMediaType == NULL) {
|
| 3697 | m_dwFlags &= ~Sample_TypeChanged;
|
| 3698 | return E_OUTOFMEMORY;
|
| 3699 | }
|
| 3700 |
|
| 3701 | m_dwFlags |= Sample_TypeChanged;
|
| 3702 | return NOERROR;
|
| 3703 | }
|
| 3704 |
|
| 3705 | // Set and get properties (IMediaSample2)
|
| 3706 | STDMETHODIMP CMediaSample::GetProperties(
|
| 3707 | DWORD cbProperties,
|
| 3708 | __out_bcount(cbProperties) BYTE * pbProperties
|
| 3709 | )
|
| 3710 | {
|
| 3711 | if (0 != cbProperties) {
|
| 3712 | CheckPointer(pbProperties, E_POINTER);
|
| 3713 | // Return generic stuff up to the length
|
| 3714 | AM_SAMPLE2_PROPERTIES Props;
|
| 3715 | Props.cbData = min(cbProperties, sizeof(Props));
|
| 3716 | Props.dwSampleFlags = m_dwFlags & ~Sample_MediaTimeValid;
|
| 3717 | Props.dwTypeSpecificFlags = m_dwTypeSpecificFlags;
|
| 3718 | Props.pbBuffer = m_pBuffer;
|
| 3719 | Props.cbBuffer = m_cbBuffer;
|
| 3720 | Props.lActual = m_lActual;
|
| 3721 | Props.tStart = m_Start;
|
| 3722 | Props.tStop = m_End;
|
| 3723 | Props.dwStreamId = m_dwStreamId;
|
| 3724 | if (m_dwFlags & AM_SAMPLE_TYPECHANGED) {
|
| 3725 | Props.pMediaType = m_pMediaType;
|
| 3726 | } else {
|
| 3727 | Props.pMediaType = NULL;
|
| 3728 | }
|
| 3729 | CopyMemory(pbProperties, &Props, Props.cbData);
|
| 3730 | }
|
| 3731 | return S_OK;
|
| 3732 | }
|
| 3733 |
|
| 3734 | #define CONTAINS_FIELD(type, field, offset) \
|
| 3735 | ((FIELD_OFFSET(type, field) + sizeof(((type *)0)->field)) <= offset)
|
| 3736 |
|
| 3737 | HRESULT CMediaSample::SetProperties(
|
| 3738 | DWORD cbProperties,
|
| 3739 | __in_bcount(cbProperties) const BYTE * pbProperties
|
| 3740 | )
|
| 3741 | {
|
| 3742 |
|
| 3743 | /* Generic properties */
|
| 3744 | AM_MEDIA_TYPE *pMediaType = NULL;
|
| 3745 |
|
| 3746 | if (CONTAINS_FIELD(AM_SAMPLE2_PROPERTIES, cbData, cbProperties)) {
|
| 3747 | CheckPointer(pbProperties, E_POINTER);
|
| 3748 | AM_SAMPLE2_PROPERTIES *pProps =
|
| 3749 | (AM_SAMPLE2_PROPERTIES *)pbProperties;
|
| 3750 |
|
| 3751 | /* Don't use more data than is actually there */
|
| 3752 | if (pProps->cbData < cbProperties) {
|
| 3753 | cbProperties = pProps->cbData;
|
| 3754 | }
|
| 3755 | /* We only handle IMediaSample2 */
|
| 3756 | if (cbProperties > sizeof(*pProps) ||
|
| 3757 | pProps->cbData > sizeof(*pProps)) {
|
| 3758 | return E_INVALIDARG;
|
| 3759 | }
|
| 3760 | /* Do checks first, the assignments (for backout) */
|
| 3761 | if (CONTAINS_FIELD(AM_SAMPLE2_PROPERTIES, dwSampleFlags, cbProperties)) {
|
| 3762 | /* Check the flags */
|
| 3763 | if (pProps->dwSampleFlags &
|
| 3764 | (~Sample_ValidFlags | Sample_MediaTimeValid)) {
|
| 3765 | return E_INVALIDARG;
|
| 3766 | }
|
| 3767 | /* Check a flag isn't being set for a property
|
| 3768 | not being provided
|
| 3769 | */
|
| 3770 | if ((pProps->dwSampleFlags & AM_SAMPLE_TIMEVALID) &&
|
| 3771 | !(m_dwFlags & AM_SAMPLE_TIMEVALID) &&
|
| 3772 | !CONTAINS_FIELD(AM_SAMPLE2_PROPERTIES, tStop, cbProperties)) {
|
| 3773 | return E_INVALIDARG;
|
| 3774 | }
|
| 3775 | }
|
| 3776 | /* NB - can't SET the pointer or size */
|
| 3777 | if (CONTAINS_FIELD(AM_SAMPLE2_PROPERTIES, pbBuffer, cbProperties)) {
|
| 3778 |
|
| 3779 | /* Check pbBuffer */
|
| 3780 | if (pProps->pbBuffer != 0 && pProps->pbBuffer != m_pBuffer) {
|
| 3781 | return E_INVALIDARG;
|
| 3782 | }
|
| 3783 | }
|
| 3784 | if (CONTAINS_FIELD(AM_SAMPLE2_PROPERTIES, cbBuffer, cbProperties)) {
|
| 3785 |
|
| 3786 | /* Check cbBuffer */
|
| 3787 | if (pProps->cbBuffer != 0 && pProps->cbBuffer != m_cbBuffer) {
|
| 3788 | return E_INVALIDARG;
|
| 3789 | }
|
| 3790 | }
|
| 3791 | if (CONTAINS_FIELD(AM_SAMPLE2_PROPERTIES, cbBuffer, cbProperties) &&
|
| 3792 | CONTAINS_FIELD(AM_SAMPLE2_PROPERTIES, lActual, cbProperties)) {
|
| 3793 |
|
| 3794 | /* Check lActual */
|
| 3795 | if (pProps->cbBuffer < pProps->lActual) {
|
| 3796 | return E_INVALIDARG;
|
| 3797 | }
|
| 3798 | }
|
| 3799 |
|
| 3800 | if (CONTAINS_FIELD(AM_SAMPLE2_PROPERTIES, pMediaType, cbProperties)) {
|
| 3801 |
|
| 3802 | /* Check pMediaType */
|
| 3803 | if (pProps->dwSampleFlags & AM_SAMPLE_TYPECHANGED) {
|
| 3804 | CheckPointer(pProps->pMediaType, E_POINTER);
|
| 3805 | pMediaType = CreateMediaType(pProps->pMediaType);
|
| 3806 | if (pMediaType == NULL) {
|
| 3807 | return E_OUTOFMEMORY;
|
| 3808 | }
|
| 3809 | }
|
| 3810 | }
|
| 3811 |
|
| 3812 | /* Now do the assignments */
|
| 3813 | if (CONTAINS_FIELD(AM_SAMPLE2_PROPERTIES, dwStreamId, cbProperties)) {
|
| 3814 | m_dwStreamId = pProps->dwStreamId;
|
| 3815 | }
|
| 3816 | if (CONTAINS_FIELD(AM_SAMPLE2_PROPERTIES, dwSampleFlags, cbProperties)) {
|
| 3817 | /* Set the flags */
|
| 3818 | m_dwFlags = pProps->dwSampleFlags |
|
| 3819 | (m_dwFlags & Sample_MediaTimeValid);
|
| 3820 | m_dwTypeSpecificFlags = pProps->dwTypeSpecificFlags;
|
| 3821 | } else {
|
| 3822 | if (CONTAINS_FIELD(AM_SAMPLE2_PROPERTIES, dwTypeSpecificFlags, cbProperties)) {
|
| 3823 | m_dwTypeSpecificFlags = pProps->dwTypeSpecificFlags;
|
| 3824 | }
|
| 3825 | }
|
| 3826 |
|
| 3827 | if (CONTAINS_FIELD(AM_SAMPLE2_PROPERTIES, lActual, cbProperties)) {
|
| 3828 | /* Set lActual */
|
| 3829 | m_lActual = pProps->lActual;
|
| 3830 | }
|
| 3831 |
|
| 3832 | if (CONTAINS_FIELD(AM_SAMPLE2_PROPERTIES, tStop, cbProperties)) {
|
| 3833 |
|
| 3834 | /* Set the times */
|
| 3835 | m_End = pProps->tStop;
|
| 3836 | }
|
| 3837 | if (CONTAINS_FIELD(AM_SAMPLE2_PROPERTIES, tStart, cbProperties)) {
|
| 3838 |
|
| 3839 | /* Set the times */
|
| 3840 | m_Start = pProps->tStart;
|
| 3841 | }
|
| 3842 |
|
| 3843 | if (CONTAINS_FIELD(AM_SAMPLE2_PROPERTIES, pMediaType, cbProperties)) {
|
| 3844 | /* Set pMediaType */
|
| 3845 | if (pProps->dwSampleFlags & AM_SAMPLE_TYPECHANGED) {
|
| 3846 | if (m_pMediaType != NULL) {
|
| 3847 | DeleteMediaType(m_pMediaType);
|
| 3848 | }
|
| 3849 | m_pMediaType = pMediaType;
|
| 3850 | }
|
| 3851 | }
|
| 3852 |
|
| 3853 | /* Fix up the type changed flag to correctly reflect the current state
|
| 3854 | If, for instance the input contained no type change but the
|
| 3855 | output does then if we don't do this we'd lose the
|
| 3856 | output media type.
|
| 3857 | */
|
| 3858 | if (m_pMediaType) {
|
| 3859 | m_dwFlags |= Sample_TypeChanged;
|
| 3860 | } else {
|
| 3861 | m_dwFlags &= ~Sample_TypeChanged;
|
| 3862 | }
|
| 3863 | }
|
| 3864 |
|
| 3865 | return S_OK;
|
| 3866 | }
|
| 3867 |
|
| 3868 |
|
| 3869 | //
|
| 3870 | // The streaming thread calls IPin::NewSegment(), IPin::EndOfStream(),
|
| 3871 | // IMemInputPin::Receive() and IMemInputPin::ReceiveMultiple() on the
|
| 3872 | // connected input pin. The application thread calls Block(). The
|
| 3873 | // following class members can only be called by the streaming thread.
|
| 3874 | //
|
| 3875 | // Deliver()
|
| 3876 | // DeliverNewSegment()
|
| 3877 | // StartUsingOutputPin()
|
| 3878 | // StopUsingOutputPin()
|
| 3879 | // ChangeOutputFormat()
|
| 3880 | // ChangeMediaType()
|
| 3881 | // DynamicReconnect()
|
| 3882 | //
|
| 3883 | // The following class members can only be called by the application thread.
|
| 3884 | //
|
| 3885 | // Block()
|
| 3886 | // SynchronousBlockOutputPin()
|
| 3887 | // AsynchronousBlockOutputPin()
|
| 3888 | //
|
| 3889 |
|
| 3890 | CDynamicOutputPin::CDynamicOutputPin(
|
| 3891 | __in_opt LPCTSTR pObjectName,
|
| 3892 | __in CBaseFilter *pFilter,
|
| 3893 | __in CCritSec *pLock,
|
| 3894 | __inout HRESULT *phr,
|
| 3895 | __in_opt LPCWSTR pName) :
|
| 3896 | CBaseOutputPin(pObjectName, pFilter, pLock, phr, pName),
|
| 3897 | m_hStopEvent(NULL),
|
| 3898 | m_pGraphConfig(NULL),
|
| 3899 | m_bPinUsesReadOnlyAllocator(FALSE),
|
| 3900 | m_BlockState(NOT_BLOCKED),
|
| 3901 | m_hUnblockOutputPinEvent(NULL),
|
| 3902 | m_hNotifyCallerPinBlockedEvent(NULL),
|
| 3903 | m_dwBlockCallerThreadID(0),
|
| 3904 | m_dwNumOutstandingOutputPinUsers(0)
|
| 3905 | {
|
| 3906 | HRESULT hr = Initialize();
|
| 3907 | if( FAILED( hr ) ) {
|
| 3908 | *phr = hr;
|
| 3909 | return;
|
| 3910 | }
|
| 3911 | }
|
| 3912 |
|
| 3913 | #ifdef UNICODE
|
| 3914 | CDynamicOutputPin::CDynamicOutputPin(
|
| 3915 | __in_opt LPCSTR pObjectName,
|
| 3916 | __in CBaseFilter *pFilter,
|
| 3917 | __in CCritSec *pLock,
|
| 3918 | __inout HRESULT *phr,
|
| 3919 | __in_opt LPCWSTR pName) :
|
| 3920 | CBaseOutputPin(pObjectName, pFilter, pLock, phr, pName),
|
| 3921 | m_hStopEvent(NULL),
|
| 3922 | m_pGraphConfig(NULL),
|
| 3923 | m_bPinUsesReadOnlyAllocator(FALSE),
|
| 3924 | m_BlockState(NOT_BLOCKED),
|
| 3925 | m_hUnblockOutputPinEvent(NULL),
|
| 3926 | m_hNotifyCallerPinBlockedEvent(NULL),
|
| 3927 | m_dwBlockCallerThreadID(0),
|
| 3928 | m_dwNumOutstandingOutputPinUsers(0)
|
| 3929 | {
|
| 3930 | HRESULT hr = Initialize();
|
| 3931 | if( FAILED( hr ) ) {
|
| 3932 | *phr = hr;
|
| 3933 | return;
|
| 3934 | }
|
| 3935 | }
|
| 3936 | #endif
|
| 3937 |
|
| 3938 | CDynamicOutputPin::~CDynamicOutputPin()
|
| 3939 | {
|
| 3940 | if(NULL != m_hUnblockOutputPinEvent) {
|
| 3941 | // This call should not fail because we have access to m_hUnblockOutputPinEvent
|
| 3942 | // and m_hUnblockOutputPinEvent is a valid event.
|
| 3943 | EXECUTE_ASSERT(::CloseHandle(m_hUnblockOutputPinEvent));
|
| 3944 | }
|
| 3945 |
|
| 3946 | if(NULL != m_hNotifyCallerPinBlockedEvent) {
|
| 3947 | // This call should not fail because we have access to m_hNotifyCallerPinBlockedEvent
|
| 3948 | // and m_hNotifyCallerPinBlockedEvent is a valid event.
|
| 3949 | EXECUTE_ASSERT(::CloseHandle(m_hNotifyCallerPinBlockedEvent));
|
| 3950 | }
|
| 3951 | }
|
| 3952 |
|
| 3953 | HRESULT CDynamicOutputPin::Initialize(void)
|
| 3954 | {
|
| 3955 | m_hUnblockOutputPinEvent = ::CreateEvent( NULL, // The event will have the default security descriptor.
|
| 3956 | TRUE, // This is a manual reset event.
|
| 3957 | TRUE, // The event is initially signaled.
|
| 3958 | NULL ); // The event is not named.
|
| 3959 |
|
| 3960 | // CreateEvent() returns NULL if an error occurs.
|
| 3961 | if(NULL == m_hUnblockOutputPinEvent) {
|
| 3962 | return AmGetLastErrorToHResult();
|
| 3963 | }
|
| 3964 |
|
| 3965 | // Set flag to say we can reconnect while streaming.
|
| 3966 | SetReconnectWhenActive(true);
|
| 3967 |
|
| 3968 | return S_OK;
|
| 3969 | }
|
| 3970 |
|
| 3971 | STDMETHODIMP CDynamicOutputPin::NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv)
|
| 3972 | {
|
| 3973 | if(riid == IID_IPinFlowControl) {
|
| 3974 | return GetInterface(static_cast<IPinFlowControl*>(this), ppv);
|
| 3975 | } else {
|
| 3976 | return CBaseOutputPin::NonDelegatingQueryInterface(riid, ppv);
|
| 3977 | }
|
| 3978 | }
|
| 3979 |
|
| 3980 | STDMETHODIMP CDynamicOutputPin::Disconnect(void)
|
| 3981 | {
|
| 3982 | CAutoLock cObjectLock(m_pLock);
|
| 3983 | return DisconnectInternal();
|
| 3984 | }
|
| 3985 |
|
| 3986 | STDMETHODIMP CDynamicOutputPin::Block(DWORD dwBlockFlags, HANDLE hEvent)
|
| 3987 | {
|
| 3988 | const DWORD VALID_FLAGS = AM_PIN_FLOW_CONTROL_BLOCK;
|
| 3989 |
|
| 3990 | // Check for illegal flags.
|
| 3991 | if(dwBlockFlags & ~VALID_FLAGS) {
|
| 3992 | return E_INVALIDARG;
|
| 3993 | }
|
| 3994 |
|
| 3995 | // Make sure the event is unsignaled.
|
| 3996 | if((dwBlockFlags & AM_PIN_FLOW_CONTROL_BLOCK) && (NULL != hEvent)) {
|
| 3997 | if( !::ResetEvent( hEvent ) ) {
|
| 3998 | return AmGetLastErrorToHResult();
|
| 3999 | }
|
| 4000 | }
|
| 4001 |
|
| 4002 | // No flags are set if we are unblocking the output pin.
|
| 4003 | if(0 == dwBlockFlags) {
|
| 4004 |
|
| 4005 | // This parameter should be NULL because unblock operations are always synchronous.
|
| 4006 | // There is no need to notify the caller when the event is done.
|
| 4007 | if(NULL != hEvent) {
|
| 4008 | return E_INVALIDARG;
|
| 4009 | }
|
| 4010 | }
|
| 4011 |
|
| 4012 | #ifdef DEBUG
|
| 4013 | AssertValid();
|
| 4014 | #endif // DEBUG
|
| 4015 |
|
| 4016 | HRESULT hr;
|
| 4017 |
|
| 4018 | if(dwBlockFlags & AM_PIN_FLOW_CONTROL_BLOCK) {
|
| 4019 | // IPinFlowControl::Block()'s hEvent parameter is NULL if the block is synchronous.
|
| 4020 | // If hEvent is not NULL, the block is asynchronous.
|
| 4021 | if(NULL == hEvent) {
|
| 4022 | hr = SynchronousBlockOutputPin();
|
| 4023 | } else {
|
| 4024 | hr = AsynchronousBlockOutputPin(hEvent);
|
| 4025 | }
|
| 4026 | } else {
|
| 4027 | hr = UnblockOutputPin();
|
| 4028 | }
|
| 4029 |
|
| 4030 | #ifdef DEBUG
|
| 4031 | AssertValid();
|
| 4032 | #endif // DEBUG
|
| 4033 |
|
| 4034 | if(FAILED(hr)) {
|
| 4035 | return hr;
|
| 4036 | }
|
| 4037 |
|
| 4038 | return S_OK;
|
| 4039 | }
|
| 4040 |
|
| 4041 | HRESULT CDynamicOutputPin::SynchronousBlockOutputPin(void)
|
| 4042 | {
|
| 4043 | HANDLE hNotifyCallerPinBlockedEvent = :: CreateEvent( NULL, // The event will have the default security attributes.
|
| 4044 | FALSE, // This is an automatic reset event.
|
| 4045 | FALSE, // The event is initially unsignaled.
|
| 4046 | NULL ); // The event is not named.
|
| 4047 |
|
| 4048 | // CreateEvent() returns NULL if an error occurs.
|
| 4049 | if(NULL == hNotifyCallerPinBlockedEvent) {
|
| 4050 | return AmGetLastErrorToHResult();
|
| 4051 | }
|
| 4052 |
|
| 4053 | HRESULT hr = AsynchronousBlockOutputPin(hNotifyCallerPinBlockedEvent);
|
| 4054 | if(FAILED(hr)) {
|
| 4055 | // This call should not fail because we have access to hNotifyCallerPinBlockedEvent
|
| 4056 | // and hNotifyCallerPinBlockedEvent is a valid event.
|
| 4057 | EXECUTE_ASSERT(::CloseHandle(hNotifyCallerPinBlockedEvent));
|
| 4058 |
|
| 4059 | return hr;
|
| 4060 | }
|
| 4061 |
|
| 4062 | hr = WaitEvent(hNotifyCallerPinBlockedEvent);
|
| 4063 |
|
| 4064 | // This call should not fail because we have access to hNotifyCallerPinBlockedEvent
|
| 4065 | // and hNotifyCallerPinBlockedEvent is a valid event.
|
| 4066 | EXECUTE_ASSERT(::CloseHandle(hNotifyCallerPinBlockedEvent));
|
| 4067 |
|
| 4068 | if(FAILED(hr)) {
|
| 4069 | return hr;
|
| 4070 | }
|
| 4071 |
|
| 4072 | return S_OK;
|
| 4073 | }
|
| 4074 |
|
| 4075 | HRESULT CDynamicOutputPin::AsynchronousBlockOutputPin(HANDLE hNotifyCallerPinBlockedEvent)
|
| 4076 | {
|
| 4077 | // This function holds the m_BlockStateLock because it uses
|
| 4078 | // m_dwBlockCallerThreadID, m_BlockState and
|
| 4079 | // m_hNotifyCallerPinBlockedEvent.
|
| 4080 | CAutoLock alBlockStateLock(&m_BlockStateLock);
|
| 4081 |
|
| 4082 | if(NOT_BLOCKED != m_BlockState) {
|
| 4083 | if(m_dwBlockCallerThreadID == ::GetCurrentThreadId()) {
|
| 4084 | return VFW_E_PIN_ALREADY_BLOCKED_ON_THIS_THREAD;
|
| 4085 | } else {
|
| 4086 | return VFW_E_PIN_ALREADY_BLOCKED;
|
| 4087 | }
|
| 4088 | }
|
| 4089 |
|
| 4090 | BOOL fSuccess = ::DuplicateHandle( ::GetCurrentProcess(),
|
| 4091 | hNotifyCallerPinBlockedEvent,
|
| 4092 | ::GetCurrentProcess(),
|
| 4093 | &m_hNotifyCallerPinBlockedEvent,
|
| 4094 | EVENT_MODIFY_STATE,
|
| 4095 | FALSE,
|
| 4096 | 0 );
|
| 4097 | if( !fSuccess ) {
|
| 4098 | return AmGetLastErrorToHResult();
|
| 4099 | }
|
| 4100 |
|
| 4101 | m_BlockState = PENDING;
|
| 4102 | m_dwBlockCallerThreadID = ::GetCurrentThreadId();
|
| 4103 |
|
| 4104 | // The output pin cannot be blocked if the streaming thread is
|
| 4105 | // calling IPin::NewSegment(), IPin::EndOfStream(), IMemInputPin::Receive()
|
| 4106 | // or IMemInputPin::ReceiveMultiple() on the connected input pin. Also, it
|
| 4107 | // cannot be blocked if the streaming thread is calling DynamicReconnect(),
|
| 4108 | // ChangeMediaType() or ChangeOutputFormat().
|
| 4109 | if(!StreamingThreadUsingOutputPin()) {
|
| 4110 |
|
| 4111 | // The output pin can be immediately blocked.
|
| 4112 | BlockOutputPin();
|
| 4113 | }
|
| 4114 |
|
| 4115 | return S_OK;
|
| 4116 | }
|
| 4117 |
|
| 4118 | void CDynamicOutputPin::BlockOutputPin(void)
|
| 4119 | {
|
| 4120 | // The caller should always hold the m_BlockStateLock because this function
|
| 4121 | // uses m_BlockState and m_hNotifyCallerPinBlockedEvent.
|
| 4122 | ASSERT(CritCheckIn(&m_BlockStateLock));
|
| 4123 |
|
| 4124 | // This function should not be called if the streaming thread is modifying
|
| 4125 | // the connection state or it's passing data downstream.
|
| 4126 | ASSERT(!StreamingThreadUsingOutputPin());
|
| 4127 |
|
| 4128 | // This should not fail because we successfully created the event
|
| 4129 | // and we have the security permissions to change it's state.
|
| 4130 | EXECUTE_ASSERT(::ResetEvent(m_hUnblockOutputPinEvent));
|
| 4131 |
|
| 4132 | // This event should not fail because AsynchronousBlockOutputPin() successfully
|
| 4133 | // duplicated this handle and we have the appropriate security permissions.
|
| 4134 | EXECUTE_ASSERT(::SetEvent(m_hNotifyCallerPinBlockedEvent));
|
| 4135 | EXECUTE_ASSERT(::CloseHandle(m_hNotifyCallerPinBlockedEvent));
|
| 4136 |
|
| 4137 | m_BlockState = BLOCKED;
|
| 4138 | m_hNotifyCallerPinBlockedEvent = NULL;
|
| 4139 | }
|
| 4140 |
|
| 4141 | HRESULT CDynamicOutputPin::UnblockOutputPin(void)
|
| 4142 | {
|
| 4143 | // UnblockOutputPin() holds the m_BlockStateLock because it
|
| 4144 | // uses m_BlockState, m_dwBlockCallerThreadID and
|
| 4145 | // m_hNotifyCallerPinBlockedEvent.
|
| 4146 | CAutoLock alBlockStateLock(&m_BlockStateLock);
|
| 4147 |
|
| 4148 | if(NOT_BLOCKED == m_BlockState) {
|
| 4149 | return S_FALSE;
|
| 4150 | }
|
| 4151 |
|
| 4152 | // This should not fail because we successfully created the event
|
| 4153 | // and we have the security permissions to change it's state.
|
| 4154 | EXECUTE_ASSERT(::SetEvent(m_hUnblockOutputPinEvent));
|
| 4155 |
|
| 4156 | // Cancel the block operation if it's still pending.
|
| 4157 | if(NULL != m_hNotifyCallerPinBlockedEvent) {
|
| 4158 | // This event should not fail because AsynchronousBlockOutputPin() successfully
|
| 4159 | // duplicated this handle and we have the appropriate security permissions.
|
| 4160 | EXECUTE_ASSERT(::SetEvent(m_hNotifyCallerPinBlockedEvent));
|
| 4161 | EXECUTE_ASSERT(::CloseHandle(m_hNotifyCallerPinBlockedEvent));
|
| 4162 | }
|
| 4163 |
|
| 4164 | m_BlockState = NOT_BLOCKED;
|
| 4165 | m_dwBlockCallerThreadID = 0;
|
| 4166 | m_hNotifyCallerPinBlockedEvent = NULL;
|
| 4167 |
|
| 4168 | return S_OK;
|
| 4169 | }
|
| 4170 |
|
| 4171 | HRESULT CDynamicOutputPin::StartUsingOutputPin(void)
|
| 4172 | {
|
| 4173 | // The caller should not hold m_BlockStateLock. If the caller does,
|
| 4174 | // a deadlock could occur.
|
| 4175 | ASSERT(CritCheckOut(&m_BlockStateLock));
|
| 4176 |
|
| 4177 | CAutoLock alBlockStateLock(&m_BlockStateLock);
|
| 4178 |
|
| 4179 | #ifdef DEBUG
|
| 4180 | AssertValid();
|
| 4181 | #endif // DEBUG
|
| 4182 |
|
| 4183 | // Are we in the middle of a block operation?
|
| 4184 | while(BLOCKED == m_BlockState) {
|
| 4185 | m_BlockStateLock.Unlock();
|
| 4186 |
|
| 4187 | // If this ASSERT fires, a deadlock could occur. The caller should make sure
|
| 4188 | // that this thread never acquires the Block State lock more than once.
|
| 4189 | ASSERT(CritCheckOut( &m_BlockStateLock ));
|
| 4190 |
|
| 4191 | // WaitForMultipleObjects() returns WAIT_OBJECT_0 if the unblock event
|
| 4192 | // is fired. It returns WAIT_OBJECT_0 + 1 if the stop event if fired.
|
| 4193 | // See the Windows SDK documentation for more information on
|
| 4194 | // WaitForMultipleObjects().
|
| 4195 | const DWORD UNBLOCK = WAIT_OBJECT_0;
|
| 4196 | const DWORD STOP = WAIT_OBJECT_0 + 1;
|
| 4197 |
|
| 4198 | HANDLE ahWaitEvents[] = { m_hUnblockOutputPinEvent, m_hStopEvent };
|
| 4199 | DWORD dwNumWaitEvents = sizeof(ahWaitEvents)/sizeof(HANDLE);
|
| 4200 |
|
| 4201 | DWORD dwReturnValue = ::WaitForMultipleObjects( dwNumWaitEvents, ahWaitEvents, FALSE, INFINITE );
|
| 4202 |
|
| 4203 | m_BlockStateLock.Lock();
|
| 4204 |
|
| 4205 | #ifdef DEBUG
|
| 4206 | AssertValid();
|
| 4207 | #endif // DEBUG
|
| 4208 |
|
| 4209 | switch( dwReturnValue ) {
|
| 4210 | case UNBLOCK:
|
| 4211 | break;
|
| 4212 |
|
| 4213 | case STOP:
|
| 4214 | return VFW_E_STATE_CHANGED;
|
| 4215 |
|
| 4216 | case WAIT_FAILED:
|
| 4217 | return AmGetLastErrorToHResult();
|
| 4218 |
|
| 4219 | default:
|
| 4220 | DbgBreak( "An Unexpected case occured in CDynamicOutputPin::StartUsingOutputPin()." );
|
| 4221 | return E_UNEXPECTED;
|
| 4222 | }
|
| 4223 | }
|
| 4224 |
|
| 4225 | m_dwNumOutstandingOutputPinUsers++;
|
| 4226 |
|
| 4227 | #ifdef DEBUG
|
| 4228 | AssertValid();
|
| 4229 | #endif // DEBUG
|
| 4230 |
|
| 4231 | return S_OK;
|
| 4232 | }
|
| 4233 |
|
| 4234 | void CDynamicOutputPin::StopUsingOutputPin(void)
|
| 4235 | {
|
| 4236 | CAutoLock alBlockStateLock(&m_BlockStateLock);
|
| 4237 |
|
| 4238 | #ifdef DEBUG
|
| 4239 | AssertValid();
|
| 4240 | #endif // DEBUG
|
| 4241 |
|
| 4242 | m_dwNumOutstandingOutputPinUsers--;
|
| 4243 |
|
| 4244 | if((m_dwNumOutstandingOutputPinUsers == 0) && (NOT_BLOCKED != m_BlockState)) {
|
| 4245 | BlockOutputPin();
|
| 4246 | }
|
| 4247 |
|
| 4248 | #ifdef DEBUG
|
| 4249 | AssertValid();
|
| 4250 | #endif // DEBUG
|
| 4251 | }
|
| 4252 |
|
| 4253 | bool CDynamicOutputPin::StreamingThreadUsingOutputPin(void)
|
| 4254 | {
|
| 4255 | CAutoLock alBlockStateLock(&m_BlockStateLock);
|
| 4256 |
|
| 4257 | return (m_dwNumOutstandingOutputPinUsers > 0);
|
| 4258 | }
|
| 4259 |
|
| 4260 | void CDynamicOutputPin::SetConfigInfo(IGraphConfig *pGraphConfig, HANDLE hStopEvent)
|
| 4261 | {
|
| 4262 | // This pointer is not addrefed because filters are not allowed to
|
| 4263 | // hold references to the filter graph manager. See the documentation for
|
| 4264 | // IBaseFilter::JoinFilterGraph() in the Direct Show SDK for more information.
|
| 4265 | m_pGraphConfig = pGraphConfig;
|
| 4266 |
|
| 4267 | m_hStopEvent = hStopEvent;
|
| 4268 | }
|
| 4269 |
|
| 4270 | HRESULT CDynamicOutputPin::Active(void)
|
| 4271 | {
|
| 4272 | // Make sure the user initialized the object by calling SetConfigInfo().
|
| 4273 | if((NULL == m_hStopEvent) || (NULL == m_pGraphConfig)) {
|
| 4274 | DbgBreak( ERROR: CDynamicOutputPin::Active() failed because m_pGraphConfig and m_hStopEvent were not initialized. Call SetConfigInfo() to initialize them. );
|
| 4275 | return E_FAIL;
|
| 4276 | }
|
| 4277 |
|
| 4278 | // If this ASSERT fires, the user may have passed an invalid event handle to SetConfigInfo().
|
| 4279 | // The ASSERT can also fire if the event if destroyed and then Active() is called. An event
|
| 4280 | // handle is invalid if 1) the event does not exist or the user does not have the security
|
| 4281 | // permissions to use the event.
|
| 4282 | EXECUTE_ASSERT(ResetEvent(m_hStopEvent));
|
| 4283 |
|
| 4284 | return CBaseOutputPin::Active();
|
| 4285 | }
|
| 4286 |
|
| 4287 | HRESULT CDynamicOutputPin::Inactive(void)
|
| 4288 | {
|
| 4289 | // If this ASSERT fires, the user may have passed an invalid event handle to SetConfigInfo().
|
| 4290 | // The ASSERT can also fire if the event if destroyed and then Active() is called. An event
|
| 4291 | // handle is invalid if 1) the event does not exist or the user does not have the security
|
| 4292 | // permissions to use the event.
|
| 4293 | EXECUTE_ASSERT(SetEvent(m_hStopEvent));
|
| 4294 |
|
| 4295 | return CBaseOutputPin::Inactive();
|
| 4296 | }
|
| 4297 |
|
| 4298 | HRESULT CDynamicOutputPin::DeliverBeginFlush(void)
|
| 4299 | {
|
| 4300 | // If this ASSERT fires, the user may have passed an invalid event handle to SetConfigInfo().
|
| 4301 | // The ASSERT can also fire if the event if destroyed and then DeliverBeginFlush() is called.
|
| 4302 | // An event handle is invalid if 1) the event does not exist or the user does not have the security
|
| 4303 | // permissions to use the event.
|
| 4304 | EXECUTE_ASSERT(SetEvent(m_hStopEvent));
|
| 4305 |
|
| 4306 | return CBaseOutputPin::DeliverBeginFlush();
|
| 4307 | }
|
| 4308 |
|
| 4309 | HRESULT CDynamicOutputPin::DeliverEndFlush(void)
|
| 4310 | {
|
| 4311 | // If this ASSERT fires, the user may have passed an invalid event handle to SetConfigInfo().
|
| 4312 | // The ASSERT can also fire if the event if destroyed and then DeliverBeginFlush() is called.
|
| 4313 | // An event handle is invalid if 1) the event does not exist or the user does not have the security
|
| 4314 | // permissions to use the event.
|
| 4315 | EXECUTE_ASSERT(ResetEvent(m_hStopEvent));
|
| 4316 |
|
| 4317 | return CBaseOutputPin::DeliverEndFlush();
|
| 4318 | }
|
| 4319 |
|
| 4320 |
|
| 4321 | // ChangeOutputFormat() either dynamicly changes the connection's format type or it dynamicly
|
| 4322 | // reconnects the output pin.
|
| 4323 | HRESULT CDynamicOutputPin::ChangeOutputFormat
|
| 4324 | (
|
| 4325 | const AM_MEDIA_TYPE *pmt,
|
| 4326 | REFERENCE_TIME tSegmentStart,
|
| 4327 | REFERENCE_TIME tSegmentStop,
|
| 4328 | double dSegmentRate
|
| 4329 | )
|
| 4330 | {
|
| 4331 | // The caller should call StartUsingOutputPin() before calling this
|
| 4332 | // method.
|
| 4333 | ASSERT(StreamingThreadUsingOutputPin());
|
| 4334 |
|
| 4335 | // Callers should always pass a valid media type to ChangeOutputFormat() .
|
| 4336 | ASSERT(NULL != pmt);
|
| 4337 |
|
| 4338 | CMediaType cmt(*pmt);
|
| 4339 | HRESULT hr = ChangeMediaType(&cmt);
|
| 4340 | if (FAILED(hr)) {
|
| 4341 | return hr;
|
| 4342 | }
|
| 4343 |
|
| 4344 | hr = DeliverNewSegment(tSegmentStart, tSegmentStop, dSegmentRate);
|
| 4345 | if( FAILED( hr ) ) {
|
| 4346 | return hr;
|
| 4347 | }
|
| 4348 |
|
| 4349 | return S_OK;
|
| 4350 | }
|
| 4351 |
|
| 4352 | HRESULT CDynamicOutputPin::ChangeMediaType(const CMediaType *pmt)
|
| 4353 | {
|
| 4354 | // The caller should call StartUsingOutputPin() before calling this
|
| 4355 | // method.
|
| 4356 | ASSERT(StreamingThreadUsingOutputPin());
|
| 4357 |
|
| 4358 | // This function assumes the filter graph is running.
|
| 4359 | ASSERT(!IsStopped());
|
| 4360 |
|
| 4361 | if(!IsConnected()) {
|
| 4362 | return VFW_E_NOT_CONNECTED;
|
| 4363 | }
|
| 4364 |
|
| 4365 | /* First check if the downstream pin will accept a dynamic
|
| 4366 | format change
|
| 4367 | */
|
| 4368 | QzCComPtr<IPinConnection> pConnection;
|
| 4369 |
|
| 4370 | m_Connected->QueryInterface(IID_IPinConnection, (void **)&pConnection);
|
| 4371 | if(pConnection != NULL) {
|
| 4372 |
|
| 4373 | if(S_OK == pConnection->DynamicQueryAccept(pmt)) {
|
| 4374 |
|
| 4375 | HRESULT hr = ChangeMediaTypeHelper(pmt);
|
| 4376 | if(FAILED(hr)) {
|
| 4377 | return hr;
|
| 4378 | }
|
| 4379 |
|
| 4380 | return S_OK;
|
| 4381 | }
|
| 4382 | }
|
| 4383 |
|
| 4384 | /* Can't do the dynamic connection */
|
| 4385 | return DynamicReconnect(pmt);
|
| 4386 | }
|
| 4387 |
|
| 4388 | HRESULT CDynamicOutputPin::ChangeMediaTypeHelper(const CMediaType *pmt)
|
| 4389 | {
|
| 4390 | // The caller should call StartUsingOutputPin() before calling this
|
| 4391 | // method.
|
| 4392 | ASSERT(StreamingThreadUsingOutputPin());
|
| 4393 |
|
| 4394 | HRESULT hr = m_Connected->ReceiveConnection(this, pmt);
|
| 4395 | if(FAILED(hr)) {
|
| 4396 | return hr;
|
| 4397 | }
|
| 4398 |
|
| 4399 | hr = SetMediaType(pmt);
|
| 4400 | if(FAILED(hr)) {
|
| 4401 | return hr;
|
| 4402 | }
|
| 4403 |
|
| 4404 | // Does this pin use the local memory transport?
|
| 4405 | if(NULL != m_pInputPin) {
|
| 4406 | // This function assumes that m_pInputPin and m_Connected are
|
| 4407 | // two different interfaces to the same object.
|
| 4408 | ASSERT(::IsEqualObject(m_Connected, m_pInputPin));
|
| 4409 |
|
| 4410 | ALLOCATOR_PROPERTIES apInputPinRequirements;
|
| 4411 | apInputPinRequirements.cbAlign = 0;
|
| 4412 | apInputPinRequirements.cbBuffer = 0;
|
| 4413 | apInputPinRequirements.cbPrefix = 0;
|
| 4414 | apInputPinRequirements.cBuffers = 0;
|
| 4415 |
|
| 4416 | m_pInputPin->GetAllocatorRequirements(&apInputPinRequirements);
|
| 4417 |
|
| 4418 | // A zero allignment does not make any sense.
|
| 4419 | if(0 == apInputPinRequirements.cbAlign) {
|
| 4420 | apInputPinRequirements.cbAlign = 1;
|
| 4421 | }
|
| 4422 |
|
| 4423 | hr = m_pAllocator->Decommit();
|
| 4424 | if(FAILED(hr)) {
|
| 4425 | return hr;
|
| 4426 | }
|
| 4427 |
|
| 4428 | hr = DecideBufferSize(m_pAllocator, &apInputPinRequirements);
|
| 4429 | if(FAILED(hr)) {
|
| 4430 | return hr;
|
| 4431 | }
|
| 4432 |
|
| 4433 | hr = m_pAllocator->Commit();
|
| 4434 | if(FAILED(hr)) {
|
| 4435 | return hr;
|
| 4436 | }
|
| 4437 |
|
| 4438 | hr = m_pInputPin->NotifyAllocator(m_pAllocator, m_bPinUsesReadOnlyAllocator);
|
| 4439 | if(FAILED(hr)) {
|
| 4440 | return hr;
|
| 4441 | }
|
| 4442 | }
|
| 4443 |
|
| 4444 | return S_OK;
|
| 4445 | }
|
| 4446 |
|
| 4447 | // this method has to be called from the thread that is pushing data,
|
| 4448 | // and it's the caller's responsibility to make sure that the thread
|
| 4449 | // has no outstand samples because they cannot be delivered after a
|
| 4450 | // reconnect
|
| 4451 | //
|
| 4452 | HRESULT CDynamicOutputPin::DynamicReconnect( const CMediaType* pmt )
|
| 4453 | {
|
| 4454 | // The caller should call StartUsingOutputPin() before calling this
|
| 4455 | // method.
|
| 4456 | ASSERT(StreamingThreadUsingOutputPin());
|
| 4457 |
|
| 4458 | if((m_pGraphConfig == NULL) || (NULL == m_hStopEvent)) {
|
| 4459 | return E_FAIL;
|
| 4460 | }
|
| 4461 |
|
| 4462 | HRESULT hr = m_pGraphConfig->Reconnect(
|
| 4463 | this,
|
| 4464 | NULL,
|
| 4465 | pmt,
|
| 4466 | NULL,
|
| 4467 | m_hStopEvent,
|
| 4468 | AM_GRAPH_CONFIG_RECONNECT_CACHE_REMOVED_FILTERS );
|
| 4469 |
|
| 4470 | return hr;
|
| 4471 | }
|
| 4472 |
|
| 4473 | HRESULT CDynamicOutputPin::CompleteConnect(IPin *pReceivePin)
|
| 4474 | {
|
| 4475 | HRESULT hr = CBaseOutputPin::CompleteConnect(pReceivePin);
|
| 4476 | if(SUCCEEDED(hr)) {
|
| 4477 | if(!IsStopped() && m_pAllocator) {
|
| 4478 | hr = m_pAllocator->Commit();
|
| 4479 | ASSERT(hr != VFW_E_ALREADY_COMMITTED);
|
| 4480 | }
|
| 4481 | }
|
| 4482 |
|
| 4483 | return hr;
|
| 4484 | }
|
| 4485 |
|
| 4486 | #ifdef DEBUG
|
| 4487 | void CDynamicOutputPin::AssertValid(void)
|
| 4488 | {
|
| 4489 | // Make sure the object was correctly initialized.
|
| 4490 |
|
| 4491 | // This ASSERT only fires if the object failed to initialize
|
| 4492 | // and the user ignored the constructor's return code (phr).
|
| 4493 | ASSERT(NULL != m_hUnblockOutputPinEvent);
|
| 4494 |
|
| 4495 | // If either of these ASSERTs fire, the user did not correctly call
|
| 4496 | // SetConfigInfo().
|
| 4497 | ASSERT(NULL != m_hStopEvent);
|
| 4498 | ASSERT(NULL != m_pGraphConfig);
|
| 4499 |
|
| 4500 | // Make sure the block state is consistent.
|
| 4501 |
|
| 4502 | CAutoLock alBlockStateLock(&m_BlockStateLock);
|
| 4503 |
|
| 4504 | // BLOCK_STATE variables only have three legal values: PENDING, BLOCKED and NOT_BLOCKED.
|
| 4505 | ASSERT((NOT_BLOCKED == m_BlockState) || (PENDING == m_BlockState) || (BLOCKED == m_BlockState));
|
| 4506 |
|
| 4507 | // m_hNotifyCallerPinBlockedEvent is only needed when a block operation cannot complete
|
| 4508 | // immediately.
|
| 4509 | ASSERT(((NULL == m_hNotifyCallerPinBlockedEvent) && (PENDING != m_BlockState)) ||
|
| 4510 | ((NULL != m_hNotifyCallerPinBlockedEvent) && (PENDING == m_BlockState)) );
|
| 4511 |
|
| 4512 | // m_dwBlockCallerThreadID should always be 0 if the pin is not blocked and
|
| 4513 | // the user is not trying to block the pin.
|
| 4514 | ASSERT((0 == m_dwBlockCallerThreadID) || (NOT_BLOCKED != m_BlockState));
|
| 4515 |
|
| 4516 | // If this ASSERT fires, the streaming thread is using the output pin and the
|
| 4517 | // output pin is blocked.
|
| 4518 | ASSERT(((0 != m_dwNumOutstandingOutputPinUsers) && (BLOCKED != m_BlockState)) ||
|
| 4519 | ((0 == m_dwNumOutstandingOutputPinUsers) && (NOT_BLOCKED != m_BlockState)) ||
|
| 4520 | ((0 == m_dwNumOutstandingOutputPinUsers) && (NOT_BLOCKED == m_BlockState)) );
|
| 4521 | }
|
| 4522 | #endif // DEBUG
|
| 4523 |
|
| 4524 | HRESULT CDynamicOutputPin::WaitEvent(HANDLE hEvent)
|
| 4525 | {
|
| 4526 | const DWORD EVENT_SIGNALED = WAIT_OBJECT_0;
|
| 4527 |
|
| 4528 | DWORD dwReturnValue = ::WaitForSingleObject(hEvent, INFINITE);
|
| 4529 |
|
| 4530 | switch( dwReturnValue ) {
|
| 4531 | case EVENT_SIGNALED:
|
| 4532 | return S_OK;
|
| 4533 |
|
| 4534 | case WAIT_FAILED:
|
| 4535 | return AmGetLastErrorToHResult();
|
| 4536 |
|
| 4537 | default:
|
| 4538 | DbgBreak( "An Unexpected case occured in CDynamicOutputPin::WaitEvent()." );
|
| 4539 | return E_UNEXPECTED;
|
| 4540 | }
|
| 4541 | }
|
| 4542 |
|
| 4543 | //=====================================================================
|
| 4544 | //=====================================================================
|
| 4545 | // Implements CBaseAllocator
|
| 4546 | //=====================================================================
|
| 4547 | //=====================================================================
|
| 4548 |
|
| 4549 |
|
| 4550 | /* Constructor overrides the default settings for the free list to request
|
| 4551 | that it be alertable (ie the list can be cast to a handle which can be
|
| 4552 | passed to WaitForSingleObject). Both of the allocator lists also ask for
|
| 4553 | object locking, the all list matches the object default settings but I
|
| 4554 | have included them here just so it is obvious what kind of list it is */
|
| 4555 |
|
| 4556 | CBaseAllocator::CBaseAllocator(__in_opt LPCTSTR pName,
|
| 4557 | __inout_opt LPUNKNOWN pUnk,
|
| 4558 | __inout HRESULT *phr,
|
| 4559 | BOOL bEvent,
|
| 4560 | BOOL fEnableReleaseCallback
|
| 4561 | ) :
|
| 4562 | CUnknown(pName, pUnk),
|
| 4563 | m_lAllocated(0),
|
| 4564 | m_bChanged(FALSE),
|
| 4565 | m_bCommitted(FALSE),
|
| 4566 | m_bDecommitInProgress(FALSE),
|
| 4567 | m_lSize(0),
|
| 4568 | m_lCount(0),
|
| 4569 | m_lAlignment(0),
|
| 4570 | m_lPrefix(0),
|
| 4571 | m_hSem(NULL),
|
| 4572 | m_lWaiting(0),
|
| 4573 | m_fEnableReleaseCallback(fEnableReleaseCallback),
|
| 4574 | m_pNotify(NULL)
|
| 4575 | {
|
| 4576 | #ifdef DXMPERF
|
| 4577 | PERFLOG_CTOR( pName ? pName : L"CBaseAllocator", (IMemAllocator *) this );
|
| 4578 | #endif // DXMPERF
|
| 4579 |
|
| 4580 | if (bEvent) {
|
| 4581 | m_hSem = CreateSemaphore(NULL, 0, 0x7FFFFFFF, NULL);
|
| 4582 | if (m_hSem == NULL) {
|
| 4583 | *phr = E_OUTOFMEMORY;
|
| 4584 | return;
|
| 4585 | }
|
| 4586 | }
|
| 4587 | }
|
| 4588 |
|
| 4589 | #ifdef UNICODE
|
| 4590 | CBaseAllocator::CBaseAllocator(__in_opt LPCSTR pName,
|
| 4591 | __inout_opt LPUNKNOWN pUnk,
|
| 4592 | __inout HRESULT *phr,
|
| 4593 | BOOL bEvent,
|
| 4594 | BOOL fEnableReleaseCallback) :
|
| 4595 | CUnknown(pName, pUnk),
|
| 4596 | m_lAllocated(0),
|
| 4597 | m_bChanged(FALSE),
|
| 4598 | m_bCommitted(FALSE),
|
| 4599 | m_bDecommitInProgress(FALSE),
|
| 4600 | m_lSize(0),
|
| 4601 | m_lCount(0),
|
| 4602 | m_lAlignment(0),
|
| 4603 | m_lPrefix(0),
|
| 4604 | m_hSem(NULL),
|
| 4605 | m_lWaiting(0),
|
| 4606 | m_fEnableReleaseCallback(fEnableReleaseCallback),
|
| 4607 | m_pNotify(NULL)
|
| 4608 | {
|
| 4609 | #ifdef DXMPERF
|
| 4610 | PERFLOG_CTOR( L"CBaseAllocator", (IMemAllocator *) this );
|
| 4611 | #endif // DXMPERF
|
| 4612 |
|
| 4613 | if (bEvent) {
|
| 4614 | m_hSem = CreateSemaphore(NULL, 0, 0x7FFFFFFF, NULL);
|
| 4615 | if (m_hSem == NULL) {
|
| 4616 | *phr = E_OUTOFMEMORY;
|
| 4617 | return;
|
| 4618 | }
|
| 4619 | }
|
| 4620 | }
|
| 4621 | #endif
|
| 4622 |
|
| 4623 | /* Destructor */
|
| 4624 |
|
| 4625 | CBaseAllocator::~CBaseAllocator()
|
| 4626 | {
|
| 4627 | // we can't call Decommit here since that would mean a call to a
|
| 4628 | // pure virtual in destructor.
|
| 4629 | // We must assume that the derived class has gone into decommit state in
|
| 4630 | // its destructor.
|
| 4631 | #ifdef DXMPERF
|
| 4632 | PERFLOG_DTOR( L"CBaseAllocator", (IMemAllocator *) this );
|
| 4633 | #endif // DXMPERF
|
| 4634 |
|
| 4635 | ASSERT(!m_bCommitted);
|
| 4636 | if (m_hSem != NULL) {
|
| 4637 | EXECUTE_ASSERT(CloseHandle(m_hSem));
|
| 4638 | }
|
| 4639 | if (m_pNotify) {
|
| 4640 | m_pNotify->Release();
|
| 4641 | }
|
| 4642 | }
|
| 4643 |
|
| 4644 |
|
| 4645 | /* Override this to publicise our interfaces */
|
| 4646 |
|
| 4647 | STDMETHODIMP
|
| 4648 | CBaseAllocator::NonDelegatingQueryInterface(REFIID riid, __deref_out void **ppv)
|
| 4649 | {
|
| 4650 | /* Do we know about this interface */
|
| 4651 |
|
| 4652 | if (riid == IID_IMemAllocator ||
|
| 4653 | riid == IID_IMemAllocatorCallbackTemp && m_fEnableReleaseCallback) {
|
| 4654 | return GetInterface((IMemAllocatorCallbackTemp *) this, ppv);
|
| 4655 | } else {
|
| 4656 | return CUnknown::NonDelegatingQueryInterface(riid, ppv);
|
| 4657 | }
|
| 4658 | }
|
| 4659 |
|
| 4660 |
|
| 4661 | /* This sets the size and count of the required samples. The memory isn't
|
| 4662 | actually allocated until Commit() is called, if memory has already been
|
| 4663 | allocated then assuming no samples are outstanding the user may call us
|
| 4664 | to change the buffering, the memory will be released in Commit() */
|
| 4665 |
|
| 4666 | STDMETHODIMP
|
| 4667 | CBaseAllocator::SetProperties(
|
| 4668 | __in ALLOCATOR_PROPERTIES* pRequest,
|
| 4669 | __out ALLOCATOR_PROPERTIES* pActual)
|
| 4670 | {
|
| 4671 | CheckPointer(pRequest, E_POINTER);
|
| 4672 | CheckPointer(pActual, E_POINTER);
|
| 4673 | ValidateReadWritePtr(pActual, sizeof(ALLOCATOR_PROPERTIES));
|
| 4674 | CAutoLock cObjectLock(this);
|
| 4675 |
|
| 4676 | ZeroMemory(pActual, sizeof(ALLOCATOR_PROPERTIES));
|
| 4677 |
|
| 4678 | ASSERT(pRequest->cbBuffer > 0);
|
| 4679 |
|
| 4680 | /* Check the alignment requested */
|
| 4681 | if (pRequest->cbAlign != 1) {
|
| 4682 | DbgLog((LOG_ERROR, 2, TEXT("Alignment requested was 0x%x, not 1"),
|
| 4683 | pRequest->cbAlign));
|
| 4684 | return VFW_E_BADALIGN;
|
| 4685 | }
|
| 4686 |
|
| 4687 | /* Can't do this if already committed, there is an argument that says we
|
| 4688 | should not reject the SetProperties call if there are buffers still
|
| 4689 | active. However this is called by the source filter, which is the same
|
| 4690 | person who is holding the samples. Therefore it is not unreasonable
|
| 4691 | for them to free all their samples before changing the requirements */
|
| 4692 |
|
| 4693 | if (m_bCommitted) {
|
| 4694 | return VFW_E_ALREADY_COMMITTED;
|
| 4695 | }
|
| 4696 |
|
| 4697 | /* Must be no outstanding buffers */
|
| 4698 |
|
| 4699 | if (m_lAllocated != m_lFree.GetCount()) {
|
| 4700 | return VFW_E_BUFFERS_OUTSTANDING;
|
| 4701 | }
|
| 4702 |
|
| 4703 | /* There isn't any real need to check the parameters as they
|
| 4704 | will just be rejected when the user finally calls Commit */
|
| 4705 |
|
| 4706 | pActual->cbBuffer = m_lSize = pRequest->cbBuffer;
|
| 4707 | pActual->cBuffers = m_lCount = pRequest->cBuffers;
|
| 4708 | pActual->cbAlign = m_lAlignment = pRequest->cbAlign;
|
| 4709 | pActual->cbPrefix = m_lPrefix = pRequest->cbPrefix;
|
| 4710 |
|
| 4711 | m_bChanged = TRUE;
|
| 4712 | return NOERROR;
|
| 4713 | }
|
| 4714 |
|
| 4715 | STDMETHODIMP
|
| 4716 | CBaseAllocator::GetProperties(
|
| 4717 | __out ALLOCATOR_PROPERTIES * pActual)
|
| 4718 | {
|
| 4719 | CheckPointer(pActual,E_POINTER);
|
| 4720 | ValidateReadWritePtr(pActual,sizeof(ALLOCATOR_PROPERTIES));
|
| 4721 |
|
| 4722 | CAutoLock cObjectLock(this);
|
| 4723 | pActual->cbBuffer = m_lSize;
|
| 4724 | pActual->cBuffers = m_lCount;
|
| 4725 | pActual->cbAlign = m_lAlignment;
|
| 4726 | pActual->cbPrefix = m_lPrefix;
|
| 4727 | return NOERROR;
|
| 4728 | }
|
| 4729 |
|
| 4730 | // get container for a sample. Blocking, synchronous call to get the
|
| 4731 | // next free buffer (as represented by an IMediaSample interface).
|
| 4732 | // on return, the time etc properties will be invalid, but the buffer
|
| 4733 | // pointer and size will be correct.
|
| 4734 |
|
| 4735 | HRESULT CBaseAllocator::GetBuffer(__deref_out IMediaSample **ppBuffer,
|
| 4736 | __in_opt REFERENCE_TIME *pStartTime,
|
| 4737 | __in_opt REFERENCE_TIME *pEndTime,
|
| 4738 | DWORD dwFlags
|
| 4739 | )
|
| 4740 | {
|
| 4741 | UNREFERENCED_PARAMETER(pStartTime);
|
| 4742 | UNREFERENCED_PARAMETER(pEndTime);
|
| 4743 | UNREFERENCED_PARAMETER(dwFlags);
|
| 4744 | CMediaSample *pSample;
|
| 4745 |
|
| 4746 | *ppBuffer = NULL;
|
| 4747 | for (;;)
|
| 4748 | {
|
| 4749 | { // scope for lock
|
| 4750 | CAutoLock cObjectLock(this);
|
| 4751 |
|
| 4752 | /* Check we are committed */
|
| 4753 | if (!m_bCommitted) {
|
| 4754 | return VFW_E_NOT_COMMITTED;
|
| 4755 | }
|
| 4756 | pSample = (CMediaSample *) m_lFree.RemoveHead();
|
| 4757 | if (pSample == NULL) {
|
| 4758 | SetWaiting();
|
| 4759 | }
|
| 4760 | }
|
| 4761 |
|
| 4762 | /* If we didn't get a sample then wait for the list to signal */
|
| 4763 |
|
| 4764 | if (pSample) {
|
| 4765 | break;
|
| 4766 | }
|
| 4767 | if (dwFlags & AM_GBF_NOWAIT) {
|
| 4768 | return VFW_E_TIMEOUT;
|
| 4769 | }
|
| 4770 | ASSERT(m_hSem != NULL);
|
| 4771 | WaitForSingleObject(m_hSem, INFINITE);
|
| 4772 | }
|
| 4773 |
|
| 4774 | /* Addref the buffer up to one. On release
|
| 4775 | back to zero instead of being deleted, it will requeue itself by
|
| 4776 | calling the ReleaseBuffer member function. NOTE the owner of a
|
| 4777 | media sample must always be derived from CBaseAllocator */
|
| 4778 |
|
| 4779 |
|
| 4780 | ASSERT(pSample->m_cRef == 0);
|
| 4781 | pSample->m_cRef = 1;
|
| 4782 | *ppBuffer = pSample;
|
| 4783 |
|
| 4784 | #ifdef DXMPERF
|
| 4785 | PERFLOG_GETBUFFER( (IMemAllocator *) this, pSample );
|
| 4786 | #endif // DXMPERF
|
| 4787 |
|
| 4788 | return NOERROR;
|
| 4789 | }
|
| 4790 |
|
| 4791 |
|
| 4792 | /* Final release of a CMediaSample will call this */
|
| 4793 |
|
| 4794 | STDMETHODIMP
|
| 4795 | CBaseAllocator::ReleaseBuffer(IMediaSample * pSample)
|
| 4796 | {
|
| 4797 | CheckPointer(pSample,E_POINTER);
|
| 4798 | ValidateReadPtr(pSample,sizeof(IMediaSample));
|
| 4799 |
|
| 4800 | #ifdef DXMPERF
|
| 4801 | PERFLOG_RELBUFFER( (IMemAllocator *) this, pSample );
|
| 4802 | #endif // DXMPERF
|
| 4803 |
|
| 4804 |
|
| 4805 | BOOL bRelease = FALSE;
|
| 4806 | {
|
| 4807 | CAutoLock cal(this);
|
| 4808 |
|
| 4809 | /* Put back on the free list */
|
| 4810 |
|
| 4811 | m_lFree.Add((CMediaSample *)pSample);
|
| 4812 | if (m_lWaiting != 0) {
|
| 4813 | NotifySample();
|
| 4814 | }
|
| 4815 |
|
| 4816 | // if there is a pending Decommit, then we need to complete it by
|
| 4817 | // calling Free() when the last buffer is placed on the free list
|
| 4818 |
|
| 4819 | LONG l1 = m_lFree.GetCount();
|
| 4820 | if (m_bDecommitInProgress && (l1 == m_lAllocated)) {
|
| 4821 | Free();
|
| 4822 | m_bDecommitInProgress = FALSE;
|
| 4823 | bRelease = TRUE;
|
| 4824 | }
|
| 4825 | }
|
| 4826 |
|
| 4827 | if (m_pNotify) {
|
| 4828 |
|
| 4829 | ASSERT(m_fEnableReleaseCallback);
|
| 4830 |
|
| 4831 | //
|
| 4832 | // Note that this is not synchronized with setting up a notification
|
| 4833 | // method.
|
| 4834 | //
|
| 4835 | m_pNotify->NotifyRelease();
|
| 4836 | }
|
| 4837 |
|
| 4838 | /* For each buffer there is one AddRef, made in GetBuffer and released
|
| 4839 | here. This may cause the allocator and all samples to be deleted */
|
| 4840 |
|
| 4841 | if (bRelease) {
|
| 4842 | Release();
|
| 4843 | }
|
| 4844 | return NOERROR;
|
| 4845 | }
|
| 4846 |
|
| 4847 | STDMETHODIMP
|
| 4848 | CBaseAllocator::SetNotify(
|
| 4849 | IMemAllocatorNotifyCallbackTemp* pNotify
|
| 4850 | )
|
| 4851 | {
|
| 4852 | ASSERT(m_fEnableReleaseCallback);
|
| 4853 | CAutoLock lck(this);
|
| 4854 | if (pNotify) {
|
| 4855 | pNotify->AddRef();
|
| 4856 | }
|
| 4857 | if (m_pNotify) {
|
| 4858 | m_pNotify->Release();
|
| 4859 | }
|
| 4860 | m_pNotify = pNotify;
|
| 4861 | return S_OK;
|
| 4862 | }
|
| 4863 |
|
| 4864 | STDMETHODIMP
|
| 4865 | CBaseAllocator::GetFreeCount(
|
| 4866 | __out LONG* plBuffersFree
|
| 4867 | )
|
| 4868 | {
|
| 4869 | ASSERT(m_fEnableReleaseCallback);
|
| 4870 | CAutoLock cObjectLock(this);
|
| 4871 | *plBuffersFree = m_lCount - m_lAllocated + m_lFree.GetCount();
|
| 4872 | return NOERROR;
|
| 4873 | }
|
| 4874 |
|
| 4875 | void
|
| 4876 | CBaseAllocator::NotifySample()
|
| 4877 | {
|
| 4878 | if (m_lWaiting != 0) {
|
| 4879 | ASSERT(m_hSem != NULL);
|
| 4880 | ReleaseSemaphore(m_hSem, m_lWaiting, 0);
|
| 4881 | m_lWaiting = 0;
|
| 4882 | }
|
| 4883 | }
|
| 4884 |
|
| 4885 | STDMETHODIMP
|
| 4886 | CBaseAllocator::Commit()
|
| 4887 | {
|
| 4888 | /* Check we are not decommitted */
|
| 4889 | CAutoLock cObjectLock(this);
|
| 4890 |
|
| 4891 | // cannot need to alloc or re-alloc if we are committed
|
| 4892 | if (m_bCommitted) {
|
| 4893 | return NOERROR;
|
| 4894 | }
|
| 4895 |
|
| 4896 | /* Allow GetBuffer calls */
|
| 4897 |
|
| 4898 | m_bCommitted = TRUE;
|
| 4899 |
|
| 4900 | // is there a pending decommit ? if so, just cancel it
|
| 4901 | if (m_bDecommitInProgress) {
|
| 4902 | m_bDecommitInProgress = FALSE;
|
| 4903 |
|
| 4904 | // don't call Alloc at this point. He cannot allow SetProperties
|
| 4905 | // between Decommit and the last free, so the buffer size cannot have
|
| 4906 | // changed. And because some of the buffers are not free yet, he
|
| 4907 | // cannot re-alloc anyway.
|
| 4908 | return NOERROR;
|
| 4909 | }
|
| 4910 |
|
| 4911 | DbgLog((LOG_MEMORY, 1, TEXT("Allocating: %ldx%ld"), m_lCount, m_lSize));
|
| 4912 |
|
| 4913 | // actually need to allocate the samples
|
| 4914 | HRESULT hr = Alloc();
|
| 4915 | if (FAILED(hr)) {
|
| 4916 | m_bCommitted = FALSE;
|
| 4917 | return hr;
|
| 4918 | }
|
| 4919 | AddRef();
|
| 4920 | return NOERROR;
|
| 4921 | }
|
| 4922 |
|
| 4923 |
|
| 4924 | STDMETHODIMP
|
| 4925 | CBaseAllocator::Decommit()
|
| 4926 | {
|
| 4927 | BOOL bRelease = FALSE;
|
| 4928 | {
|
| 4929 | /* Check we are not already decommitted */
|
| 4930 | CAutoLock cObjectLock(this);
|
| 4931 | if (m_bCommitted == FALSE) {
|
| 4932 | if (m_bDecommitInProgress == FALSE) {
|
| 4933 | return NOERROR;
|
| 4934 | }
|
| 4935 | }
|
| 4936 |
|
| 4937 | /* No more GetBuffer calls will succeed */
|
| 4938 | m_bCommitted = FALSE;
|
| 4939 |
|
| 4940 | // are any buffers outstanding?
|
| 4941 | if (m_lFree.GetCount() < m_lAllocated) {
|
| 4942 | // please complete the decommit when last buffer is freed
|
| 4943 | m_bDecommitInProgress = TRUE;
|
| 4944 | } else {
|
| 4945 | m_bDecommitInProgress = FALSE;
|
| 4946 |
|
| 4947 | // need to complete the decommit here as there are no
|
| 4948 | // outstanding buffers
|
| 4949 |
|
| 4950 | Free();
|
| 4951 | bRelease = TRUE;
|
| 4952 | }
|
| 4953 |
|
| 4954 | // Tell anyone waiting that they can go now so we can
|
| 4955 | // reject their call
|
| 4956 | #pragma warning(push)
|
| 4957 | #ifndef _PREFAST_
|
| 4958 | #pragma warning(disable:4068)
|
| 4959 | #endif
|
| 4960 | #pragma prefast(suppress:__WARNING_DEREF_NULL_PTR, "Suppress warning related to Free() invalidating 'this' which is no applicable to CBaseAllocator::Free()")
|
| 4961 | NotifySample();
|
| 4962 |
|
| 4963 | #pragma warning(pop)
|
| 4964 | }
|
| 4965 |
|
| 4966 | if (bRelease) {
|
| 4967 | Release();
|
| 4968 | }
|
| 4969 | return NOERROR;
|
| 4970 | }
|
| 4971 |
|
| 4972 |
|
| 4973 | /* Base definition of allocation which checks we are ok to go ahead and do
|
| 4974 | the full allocation. We return S_FALSE if the requirements are the same */
|
| 4975 |
|
| 4976 | HRESULT
|
| 4977 | CBaseAllocator::Alloc(void)
|
| 4978 | {
|
| 4979 | /* Error if he hasn't set the size yet */
|
| 4980 | if (m_lCount <= 0 || m_lSize <= 0 || m_lAlignment <= 0) {
|
| 4981 | return VFW_E_SIZENOTSET;
|
| 4982 | }
|
| 4983 |
|
| 4984 | /* should never get here while buffers outstanding */
|
| 4985 | ASSERT(m_lFree.GetCount() == m_lAllocated);
|
| 4986 |
|
| 4987 | /* If the requirements haven't changed then don't reallocate */
|
| 4988 | if (m_bChanged == FALSE) {
|
| 4989 | return S_FALSE;
|
| 4990 | }
|
| 4991 |
|
| 4992 | return NOERROR;
|
| 4993 | }
|
| 4994 |
|
| 4995 | /* Implement CBaseAllocator::CSampleList::Remove(pSample)
|
| 4996 | Removes pSample from the list
|
| 4997 | */
|
| 4998 | void
|
| 4999 | CBaseAllocator::CSampleList::Remove(__inout CMediaSample * pSample)
|
| 5000 | {
|
| 5001 | CMediaSample **pSearch;
|
| 5002 | for (pSearch = &m_List;
|
| 5003 | *pSearch != NULL;
|
| 5004 | pSearch = &(CBaseAllocator::NextSample(*pSearch))) {
|
| 5005 | if (*pSearch == pSample) {
|
| 5006 | *pSearch = CBaseAllocator::NextSample(pSample);
|
| 5007 | CBaseAllocator::NextSample(pSample) = NULL;
|
| 5008 | m_nOnList--;
|
| 5009 | return;
|
| 5010 | }
|
| 5011 | }
|
| 5012 | DbgBreak("Couldn't find sample in list");
|
| 5013 | }
|
| 5014 |
|
| 5015 | //=====================================================================
|
| 5016 | //=====================================================================
|
| 5017 | // Implements CMemAllocator
|
| 5018 | //=====================================================================
|
| 5019 | //=====================================================================
|
| 5020 |
|
| 5021 |
|
| 5022 | /* This goes in the factory template table to create new instances */
|
| 5023 | CUnknown *CMemAllocator::CreateInstance(__inout_opt LPUNKNOWN pUnk, __inout HRESULT *phr)
|
| 5024 | {
|
| 5025 | CUnknown *pUnkRet = new CMemAllocator(NAME("CMemAllocator"), pUnk, phr);
|
| 5026 | return pUnkRet;
|
| 5027 | }
|
| 5028 |
|
| 5029 | CMemAllocator::CMemAllocator(
|
| 5030 | __in_opt LPCTSTR pName,
|
| 5031 | __inout_opt LPUNKNOWN pUnk,
|
| 5032 | __inout HRESULT *phr)
|
| 5033 | : CBaseAllocator(pName, pUnk, phr, TRUE, TRUE),
|
| 5034 | m_pBuffer(NULL)
|
| 5035 | {
|
| 5036 | }
|
| 5037 |
|
| 5038 | #ifdef UNICODE
|
| 5039 | CMemAllocator::CMemAllocator(
|
| 5040 | __in_opt LPCSTR pName,
|
| 5041 | __inout_opt LPUNKNOWN pUnk,
|
| 5042 | __inout HRESULT *phr)
|
| 5043 | : CBaseAllocator(pName, pUnk, phr, TRUE, TRUE),
|
| 5044 | m_pBuffer(NULL)
|
| 5045 | {
|
| 5046 | }
|
| 5047 | #endif
|
| 5048 |
|
| 5049 | /* This sets the size and count of the required samples. The memory isn't
|
| 5050 | actually allocated until Commit() is called, if memory has already been
|
| 5051 | allocated then assuming no samples are outstanding the user may call us
|
| 5052 | to change the buffering, the memory will be released in Commit() */
|
| 5053 | STDMETHODIMP
|
| 5054 | CMemAllocator::SetProperties(
|
| 5055 | __in ALLOCATOR_PROPERTIES* pRequest,
|
| 5056 | __out ALLOCATOR_PROPERTIES* pActual)
|
| 5057 | {
|
| 5058 | CheckPointer(pActual,E_POINTER);
|
| 5059 | ValidateReadWritePtr(pActual,sizeof(ALLOCATOR_PROPERTIES));
|
| 5060 | CAutoLock cObjectLock(this);
|
| 5061 |
|
| 5062 | ZeroMemory(pActual, sizeof(ALLOCATOR_PROPERTIES));
|
| 5063 |
|
| 5064 | ASSERT(pRequest->cbBuffer > 0);
|
| 5065 |
|
| 5066 | SYSTEM_INFO SysInfo;
|
| 5067 | GetSystemInfo(&SysInfo);
|
| 5068 |
|
| 5069 | /* Check the alignment request is a power of 2 */
|
| 5070 | if ((-pRequest->cbAlign & pRequest->cbAlign) != pRequest->cbAlign) {
|
| 5071 | DbgLog((LOG_ERROR, 1, TEXT("Alignment requested 0x%x not a power of 2!"),
|
| 5072 | pRequest->cbAlign));
|
| 5073 | }
|
| 5074 | /* Check the alignment requested */
|
| 5075 | if (pRequest->cbAlign == 0 ||
|
| 5076 | (SysInfo.dwAllocationGranularity & (pRequest->cbAlign - 1)) != 0) {
|
| 5077 | DbgLog((LOG_ERROR, 1, TEXT("Invalid alignment 0x%x requested - granularity = 0x%x"),
|
| 5078 | pRequest->cbAlign, SysInfo.dwAllocationGranularity));
|
| 5079 | return VFW_E_BADALIGN;
|
| 5080 | }
|
| 5081 |
|
| 5082 | /* Can't do this if already committed, there is an argument that says we
|
| 5083 | should not reject the SetProperties call if there are buffers still
|
| 5084 | active. However this is called by the source filter, which is the same
|
| 5085 | person who is holding the samples. Therefore it is not unreasonable
|
| 5086 | for them to free all their samples before changing the requirements */
|
| 5087 |
|
| 5088 | if (m_bCommitted == TRUE) {
|
| 5089 | return VFW_E_ALREADY_COMMITTED;
|
| 5090 | }
|
| 5091 |
|
| 5092 | /* Must be no outstanding buffers */
|
| 5093 |
|
| 5094 | if (m_lFree.GetCount() < m_lAllocated) {
|
| 5095 | return VFW_E_BUFFERS_OUTSTANDING;
|
| 5096 | }
|
| 5097 |
|
| 5098 | /* There isn't any real need to check the parameters as they
|
| 5099 | will just be rejected when the user finally calls Commit */
|
| 5100 |
|
| 5101 | // round length up to alignment - remember that prefix is included in
|
| 5102 | // the alignment
|
| 5103 | LONG lSize = pRequest->cbBuffer + pRequest->cbPrefix;
|
| 5104 | LONG lRemainder = lSize % pRequest->cbAlign;
|
| 5105 | if (lRemainder != 0) {
|
| 5106 | lSize = lSize - lRemainder + pRequest->cbAlign;
|
| 5107 | }
|
| 5108 | pActual->cbBuffer = m_lSize = (lSize - pRequest->cbPrefix);
|
| 5109 |
|
| 5110 | pActual->cBuffers = m_lCount = pRequest->cBuffers;
|
| 5111 | pActual->cbAlign = m_lAlignment = pRequest->cbAlign;
|
| 5112 | pActual->cbPrefix = m_lPrefix = pRequest->cbPrefix;
|
| 5113 |
|
| 5114 | m_bChanged = TRUE;
|
| 5115 | return NOERROR;
|
| 5116 | }
|
| 5117 |
|
| 5118 | // override this to allocate our resources when Commit is called.
|
| 5119 | //
|
| 5120 | // note that our resources may be already allocated when this is called,
|
| 5121 | // since we don't free them on Decommit. We will only be called when in
|
| 5122 | // decommit state with all buffers free.
|
| 5123 | //
|
| 5124 | // object locked by caller
|
| 5125 | HRESULT
|
| 5126 | CMemAllocator::Alloc(void)
|
| 5127 | {
|
| 5128 | CAutoLock lck(this);
|
| 5129 |
|
| 5130 | /* Check he has called SetProperties */
|
| 5131 | HRESULT hr = CBaseAllocator::Alloc();
|
| 5132 | if (FAILED(hr)) {
|
| 5133 | return hr;
|
| 5134 | }
|
| 5135 |
|
| 5136 | /* If the requirements haven't changed then don't reallocate */
|
| 5137 | if (hr == S_FALSE) {
|
| 5138 | ASSERT(m_pBuffer);
|
| 5139 | return NOERROR;
|
| 5140 | }
|
| 5141 | ASSERT(hr == S_OK); // we use this fact in the loop below
|
| 5142 |
|
| 5143 | /* Free the old resources */
|
| 5144 | if (m_pBuffer) {
|
| 5145 | ReallyFree();
|
| 5146 | }
|
| 5147 |
|
| 5148 | /* Make sure we've got reasonable values */
|
| 5149 | if ( m_lSize < 0 || m_lPrefix < 0 || m_lCount < 0 ) {
|
| 5150 | return E_OUTOFMEMORY;
|
| 5151 | }
|
| 5152 |
|
| 5153 | /* Compute the aligned size */
|
| 5154 | LONG lAlignedSize = m_lSize + m_lPrefix;
|
| 5155 |
|
| 5156 | /* Check overflow */
|
| 5157 | if (lAlignedSize < m_lSize) {
|
| 5158 | return E_OUTOFMEMORY;
|
| 5159 | }
|
| 5160 |
|
| 5161 | if (m_lAlignment > 1) {
|
| 5162 | LONG lRemainder = lAlignedSize % m_lAlignment;
|
| 5163 | if (lRemainder != 0) {
|
| 5164 | LONG lNewSize = lAlignedSize + m_lAlignment - lRemainder;
|
| 5165 | if (lNewSize < lAlignedSize) {
|
| 5166 | return E_OUTOFMEMORY;
|
| 5167 | }
|
| 5168 | lAlignedSize = lNewSize;
|
| 5169 | }
|
| 5170 | }
|
| 5171 |
|
| 5172 | /* Create the contiguous memory block for the samples
|
| 5173 | making sure it's properly aligned (64K should be enough!)
|
| 5174 | */
|
| 5175 | ASSERT(lAlignedSize % m_lAlignment == 0);
|
| 5176 |
|
| 5177 | LONGLONG lToAllocate = m_lCount * (LONGLONG)lAlignedSize;
|
| 5178 |
|
| 5179 | /* Check overflow */
|
| 5180 | if (lToAllocate > MAXLONG) {
|
| 5181 | return E_OUTOFMEMORY;
|
| 5182 | }
|
| 5183 |
|
| 5184 | m_pBuffer = (PBYTE)VirtualAlloc(NULL,
|
| 5185 | (LONG)lToAllocate,
|
| 5186 | MEM_COMMIT,
|
| 5187 | PAGE_READWRITE);
|
| 5188 |
|
| 5189 | if (m_pBuffer == NULL) {
|
| 5190 | return E_OUTOFMEMORY;
|
| 5191 | }
|
| 5192 |
|
| 5193 | LPBYTE pNext = m_pBuffer;
|
| 5194 | CMediaSample *pSample;
|
| 5195 |
|
| 5196 | ASSERT(m_lAllocated == 0);
|
| 5197 |
|
| 5198 | // Create the new samples - we have allocated m_lSize bytes for each sample
|
| 5199 | // plus m_lPrefix bytes per sample as a prefix. We set the pointer to
|
| 5200 | // the memory after the prefix - so that GetPointer() will return a pointer
|
| 5201 | // to m_lSize bytes.
|
| 5202 | for (; m_lAllocated < m_lCount; m_lAllocated++, pNext += lAlignedSize) {
|
| 5203 |
|
| 5204 |
|
| 5205 | pSample = new CMediaSample(
|
| 5206 | NAME("Default memory media sample"),
|
| 5207 | this,
|
| 5208 | &hr,
|
| 5209 | pNext + m_lPrefix, // GetPointer() value
|
| 5210 | m_lSize); // not including prefix
|
| 5211 |
|
| 5212 | ASSERT(SUCCEEDED(hr));
|
| 5213 | if (pSample == NULL) {
|
| 5214 | return E_OUTOFMEMORY;
|
| 5215 | }
|
| 5216 |
|
| 5217 | // This CANNOT fail
|
| 5218 | m_lFree.Add(pSample);
|
| 5219 | }
|
| 5220 |
|
| 5221 | m_bChanged = FALSE;
|
| 5222 | return NOERROR;
|
| 5223 | }
|
| 5224 |
|
| 5225 |
|
| 5226 | // override this to free up any resources we have allocated.
|
| 5227 | // called from the base class on Decommit when all buffers have been
|
| 5228 | // returned to the free list.
|
| 5229 | //
|
| 5230 | // caller has already locked the object.
|
| 5231 |
|
| 5232 | // in our case, we keep the memory until we are deleted, so
|
| 5233 | // we do nothing here. The memory is deleted in the destructor by
|
| 5234 | // calling ReallyFree()
|
| 5235 | void
|
| 5236 | CMemAllocator::Free(void)
|
| 5237 | {
|
| 5238 | return;
|
| 5239 | }
|
| 5240 |
|
| 5241 |
|
| 5242 | // called from the destructor (and from Alloc if changing size/count) to
|
| 5243 | // actually free up the memory
|
| 5244 | void
|
| 5245 | CMemAllocator::ReallyFree(void)
|
| 5246 | {
|
| 5247 | /* Should never be deleting this unless all buffers are freed */
|
| 5248 |
|
| 5249 | ASSERT(m_lAllocated == m_lFree.GetCount());
|
| 5250 |
|
| 5251 | /* Free up all the CMediaSamples */
|
| 5252 |
|
| 5253 | CMediaSample *pSample;
|
| 5254 | for (;;) {
|
| 5255 | pSample = m_lFree.RemoveHead();
|
| 5256 | if (pSample != NULL) {
|
| 5257 | delete pSample;
|
| 5258 | } else {
|
| 5259 | break;
|
| 5260 | }
|
| 5261 | }
|
| 5262 |
|
| 5263 | m_lAllocated = 0;
|
| 5264 |
|
| 5265 | // free the block of buffer memory
|
| 5266 | if (m_pBuffer) {
|
| 5267 | EXECUTE_ASSERT(VirtualFree(m_pBuffer, 0, MEM_RELEASE));
|
| 5268 | m_pBuffer = NULL;
|
| 5269 | }
|
| 5270 | }
|
| 5271 |
|
| 5272 |
|
| 5273 | /* Destructor frees our memory resources */
|
| 5274 |
|
| 5275 | CMemAllocator::~CMemAllocator()
|
| 5276 | {
|
| 5277 | Decommit();
|
| 5278 | ReallyFree();
|
| 5279 | }
|
| 5280 |
|
| 5281 | // ------------------------------------------------------------------------
|
| 5282 | // filter registration through IFilterMapper. used if IFilterMapper is
|
| 5283 | // not found (Quartz 1.0 install)
|
| 5284 |
|
| 5285 | STDAPI
|
| 5286 | AMovieSetupRegisterFilter( const AMOVIESETUP_FILTER * const psetupdata
|
| 5287 | , IFilterMapper * pIFM
|
| 5288 | , BOOL bRegister )
|
| 5289 | {
|
| 5290 | DbgLog((LOG_TRACE, 3, TEXT("= AMovieSetupRegisterFilter")));
|
| 5291 |
|
| 5292 | // check we've got data
|
| 5293 | //
|
| 5294 | if( NULL == psetupdata ) return S_FALSE;
|
| 5295 |
|
| 5296 |
|
| 5297 | // unregister filter
|
| 5298 | // (as pins are subkeys of filter's CLSID key
|
| 5299 | // they do not need to be removed separately).
|
| 5300 | //
|
| 5301 | DbgLog((LOG_TRACE, 3, TEXT("= = unregister filter")));
|
| 5302 | HRESULT hr = pIFM->UnregisterFilter( *(psetupdata->clsID) );
|
| 5303 |
|
| 5304 |
|
| 5305 | if( bRegister )
|
| 5306 | {
|
| 5307 | // register filter
|
| 5308 | //
|
| 5309 | DbgLog((LOG_TRACE, 3, TEXT("= = register filter")));
|
| 5310 | hr = pIFM->RegisterFilter( *(psetupdata->clsID)
|
| 5311 | , psetupdata->strName
|
| 5312 | , psetupdata->dwMerit );
|
| 5313 | if( SUCCEEDED(hr) )
|
| 5314 | {
|
| 5315 | // all its pins
|
| 5316 | //
|
| 5317 | DbgLog((LOG_TRACE, 3, TEXT("= = register filter pins")));
|
| 5318 | for( UINT m1=0; m1 < psetupdata->nPins; m1++ )
|
| 5319 | {
|
| 5320 | hr = pIFM->RegisterPin( *(psetupdata->clsID)
|
| 5321 | , psetupdata->lpPin[m1].strName
|
| 5322 | , psetupdata->lpPin[m1].bRendered
|
| 5323 | , psetupdata->lpPin[m1].bOutput
|
| 5324 | , psetupdata->lpPin[m1].bZero
|
| 5325 | , psetupdata->lpPin[m1].bMany
|
| 5326 | , *(psetupdata->lpPin[m1].clsConnectsToFilter)
|
| 5327 | , psetupdata->lpPin[m1].strConnectsToPin );
|
| 5328 |
|
| 5329 | if( SUCCEEDED(hr) )
|
| 5330 | {
|
| 5331 | // and each pin's media types
|
| 5332 | //
|
| 5333 | DbgLog((LOG_TRACE, 3, TEXT("= = register filter pin types")));
|
| 5334 | for( UINT m2=0; m2 < psetupdata->lpPin[m1].nMediaTypes; m2++ )
|
| 5335 | {
|
| 5336 | hr = pIFM->RegisterPinType( *(psetupdata->clsID)
|
| 5337 | , psetupdata->lpPin[m1].strName
|
| 5338 | , *(psetupdata->lpPin[m1].lpMediaType[m2].clsMajorType)
|
| 5339 | , *(psetupdata->lpPin[m1].lpMediaType[m2].clsMinorType) );
|
| 5340 | if( FAILED(hr) ) break;
|
| 5341 | }
|
| 5342 | if( FAILED(hr) ) break;
|
| 5343 | }
|
| 5344 | if( FAILED(hr) ) break;
|
| 5345 | }
|
| 5346 | }
|
| 5347 | }
|
| 5348 |
|
| 5349 | // handle one acceptable "error" - that
|
| 5350 | // of filter not being registered!
|
| 5351 | // (couldn't find a suitable #define'd
|
| 5352 | // name for the error!)
|
| 5353 | //
|
| 5354 | if( 0x80070002 == hr)
|
| 5355 | return NOERROR;
|
| 5356 | else
|
| 5357 | return hr;
|
| 5358 | }
|
| 5359 |
|
| 5360 | // Remove warnings about unreferenced inline functions
|
| 5361 | #pragma warning(disable:4514)
|
| 5362 |
|
| 5363 | #endif /* PJMEDIA_VIDEO_DEV_HAS_DSHOW */
|