스트리밍 및 애플리케이션 스레드
[이 페이지와 연결된 기능인 DirectShow는 레거시 기능입니다. MediaPlayer, IMFMediaEngine 및 Media Foundation의 오디오/비디오 캡처로 대체되었습니다. 이러한 기능은 Windows 10 및 Windows 11 최적화되었습니다. 가능한 경우 새 코드가 DirectShow 대신 Media Foundation에서 MediaPlayer, IMFMediaEngine 및 오디오/비디오 캡처를 사용하는 것이 좋습니다. 가능한 경우 레거시 API를 사용하는 기존 코드를 다시 작성하여 새 API를 사용하도록 제안합니다.]
모든 DirectShow 애플리케이션에는 애플리케이션 스레드와 하나 이상의 스트리밍 스레드라는 두 개 이상의 중요한 스레드가 포함됩니다. 샘플은 스트리밍 스레드에서 전달되고 상태 변경은 애플리케이션 스레드에서 발생합니다. 기본 스트리밍 스레드는 원본 또는 파서 필터에 의해 만들어집니다. 다른 필터는 샘플을 제공하는 작업자 스레드를 만들 수 있으며 스트리밍 스레드로도 간주됩니다.
일부 메서드는 애플리케이션 스레드에서 호출되고 다른 메서드는 스트리밍 스레드에서 호출됩니다. 예를 들면 다음과 같습니다.
- 스트리밍 스레드: IMemInputPin::Receive, IMemInputPin::ReceiveMultiple, IPin::EndOfStream, IMemAllocator::GetBuffer.
- 애플리케이션 스레드: IMediaFilter::P ause, IMediaFilter::Run, IMediaFilter::Stop, IMediaSeeking::SetPositions, IPin::BeginFlush, IPin::EndFlush.
- 둘 중 하나: IPin::NewSegment.
별도의 스트리밍 스레드를 사용하면 애플리케이션 스레드가 사용자 입력을 기다리는 동안 데이터가 그래프를 통해 흐를 수 있습니다. 그러나 여러 스레드의 위험은 필터가 일시 중지할 때(애플리케이션 스레드에서) 리소스를 만들고, 스트리밍 메서드 내에서 사용하고, 중지할 때(애플리케이션 스레드에서도) 삭제할 수 있다는 것입니다. 주의하지 않으면 스트리밍 스레드가 리소스가 제거된 후 리소스를 사용하려고 할 수 있습니다. 솔루션은 중요한 섹션을 사용하여 리소스를 보호하고 스트리밍 메서드를 상태 변경과 동기화하는 것입니다.
필터 상태를 보호하려면 필터에 하나의 중요한 섹션이 필요합니다. CBaseFilter 클래스에는 이 중요한 섹션인 CBaseFilter::m_pLock 대한 멤버 변수가 있습니다. 이 중요한 섹션을 필터 잠금이라고 합니다. 또한 각 입력 핀에는 스트리밍 스레드에서 사용하는 리소스를 보호하기 위한 중요한 섹션이 필요합니다. 이러한 중요한 섹션을 스트리밍 잠금이라고 합니다. 파생 핀 클래스에서 선언해야 합니다. Windows CRITICAL_SECTION 개체를 래핑하고 CAutoLock 클래스를 사용하여 잠글 수 있는 CCritSec 클래스를 사용하는 것이 가장 쉽습니다. CCritSec 클래스는 몇 가지 유용한 디버깅 함수도 제공합니다. 자세한 내용은 중요 섹션 디버깅 함수를 참조하세요.
필터가 중지되거나 플러시되면 애플리케이션 스레드를 스트리밍 스레드와 동기화해야 합니다. 교착 상태를 방지하려면 먼저 여러 가지 이유로 차단될 수 있는 스트리밍 스레드의 차단을 해제해야 합니다.
- 할당자의 모든 샘플이 사용 중이므로 IMemAllocator::GetBuffer 메서드 내에서 샘플을 가져오기를 기다리고 있습니다.
- Receive와 같은 스트리밍 메서드에서 다른 필터가 반환되기를 기다리고 있습니다.
- 일부 리소스를 사용할 수 있도록 자체 스트리밍 방법 중 하나에서 대기 중입니다.
- 다음 샘플의 프레젠테이션 시간을 기다리는 렌더러 필터입니다.
- 일시 중지된 동안 Receive 메서드 내에서 대기하는 렌더러 필터입니다.
따라서 필터가 중지되거나 플러시되면 다음을 수행해야 합니다.
- 어떤 이유로든 보유하고 있는 샘플을 해제합니다. 이렇게 하면 GetBuffer 메서드의 차단이 해제됩니다.
- 가능한 한 빨리 모든 스트리밍 메서드에서 반환합니다. 스트리밍 메서드가 리소스를 기다리는 경우 즉시 대기를 중지해야 합니다.
- 스트리밍 스레드가 더 이상 리소스에 액세스하지 않도록 Receive에서 샘플 거부를 시작합니다. (CBaseInputPin 클래스는 이를 자동으로 처리합니다.)
- Stop 메서드는 필터의 모든 할당자를 커밋 해제해야 합니다. (CBaseInputPin 클래스는 이를 자동으로 처리합니다.)
플러시 및 중지는 모두 애플리케이션 스레드에서 발생합니다. IMediaControl::Stop 메서드에 대한 응답으로 필터가 중지됩니다. Filter Graph Manager는 렌더러에서 시작하여 원본 필터로 뒤로 작업하는 업스트림 순서로 중지 명령을 실행합니다. stop 명령은 필터의 CBaseFilter::Stop 메서드 내에서 완전히 발생합니다. 메서드가 반환될 때 필터는 중지된 상태여야 합니다.
일반적으로 검색 명령으로 인해 플러시가 발생합니다. 플러시 명령은 원본 또는 파서 필터에서 시작하여 다운스트림으로 이동합니다. 플러시는 두 단계로 수행됩니다. IPin::BeginFlush 메서드는 보류 중인 모든 데이터와 들어오는 데이터를 삭제하도록 필터에 알립니다. IPin::EndFlush 메서드는 데이터를 다시 수락하도록 필터에 신호를 보냅니다. BeginFlush 호출이 애플리케이션 스레드에 있기 때문에 플러시하려면 두 단계가 필요하며, 그 동안 스트리밍 스레드는 계속해서 데이터를 배달합니다. 따라서 일부 샘플은 BeginFlush 호출 후에 도착할 수 있습니다. 필터는 이를 삭제해야 합니다. EndFlush 호출 후에 도착하는 모든 샘플은 새로운 것으로 보장되며 전달되어야 합니다.
다음 섹션에는 교착 상태 및 경합 조건을 방지하는 방식으로 일시 중지, 수신 등과 같은 가장 중요한 필터 메서드를 구현하는 방법을 보여 주는 코드 샘플이 포함되어 있습니다. 그러나 모든 필터에는 요구 사항이 다르므로 이러한 예제를 특정 필터에 맞게 조정해야 합니다.
참고
CTransformFilter 및 CTransInPlaceFilter 기본 클래스는 이 문서에 설명된 많은 문제를 처리합니다. 변환 필터를 작성 중이고 필터가 스트리밍 메서드 내의 이벤트를 기다리지 않거나 Receive 외부의 샘플을 유지하지 않는 경우 이러한 기본 클래스로 충분해야 합니다.