샘플 및 할당자

[이 페이지와 연결된 기능인 DirectShow는 레거시 기능입니다. MediaPlayer, IMFMediaEngineMedia Foundation의 오디오/비디오 캡처로 대체되었습니다. 이러한 기능은 Windows 10 및 Windows 11 최적화되었습니다. 가능한 경우 새 코드에서 DirectShow 대신 MediaPlayer, IMFMediaEngine오디오/비디오 캡처를 사용하는 것이 좋습니다. 가능한 경우 레거시 API를 사용하는 기존 코드를 다시 작성하여 새 API를 사용하도록 제안합니다.]

핀이 미디어 데이터를 다른 핀으로 전달하면 메모리 버퍼에 대한 직접 포인터를 전달하지 않습니다. 대신 메모리를 관리하는 COM 개체에 대한 포인터를 제공합니다. 미디어 샘플이라고 하는 이 개체는 IMediaSample 인터페이스를 노출합니다. 수신 핀은 IMediaSample::GetPointer, IMediaSample::GetSize 및 IMediaSample::GetActualDataLength와 같은 IMediaSample 메서드를 호출하여 메모리 버퍼에 액세스합니다.

샘플은 출력 핀에서 입력 핀까지 항상 다운스트림으로 이동합니다. 푸시 모델에서 출력 핀은 입력 핀에서 IMemInputPin::Receive 를 호출하여 샘플을 제공합니다. 입력 핀은 데이터를 동기적으로 처리하거나(즉, Receive 메서드 내부) 작업자 스레드에서 비동기적으로 처리합니다. 입력 핀은 리소스를 기다려야 하는 경우 Receive 메서드 내에서 차단할 수 있습니다.

할당자라고 하는 또 다른 COM 개체는 미디어 샘플을 만들고 관리하는 역할을 합니다. 할당자는 IMemAllocator 인터페이스를 노출합니다. 필터에 빈 버퍼가 있는 미디어 샘플이 필요할 때마다 샘플에 대한 포인터를 반환하는 IMemAllocator::GetBuffer 메서드를 호출합니다. 모든 핀 연결은 하나의 할당자를 공유합니다. 두 핀이 연결되면 할당자를 제공할 필터를 결정합니다. 핀은 또한 할당자에 대한 속성(예: 버퍼 수 및 각 버퍼의 크기)을 설정합니다. (자세한 내용은 필터 연결 방법 및 할당자 협상을 참조하세요.)

다음 그림에서는 할당자, 미디어 샘플 및 필터 간의 관계를 보여 줍니다.

미디어 샘플 및 할당자

미디어 샘플 참조 수

할당자는 한정된 샘플 풀을 만듭니다. 언제든지 일부 샘플이 사용 중일 수 있지만 다른 샘플은 GetBuffer 호출에 사용할 수 있습니다. 할당자는 참조 계산을 사용하여 샘플을 추적합니다. GetBuffer 메서드는 참조 수가 1인 샘플을 반환합니다. 참조 수가 0으로 이동하면 샘플은 할당자의 풀로 돌아가서 다음 GetBuffer 호출에서 사용할 수 있습니다. 참조 수가 0 이상으로 유지되는 한 GetBuffer에서 샘플을 사용할 수 없습니다. 할당자에 속하는 모든 샘플이 사용 중인 경우 GetBuffer 메서드는 샘플을 사용할 수 있게 될 때까지 차단합니다.

예를 들어 입력 핀이 샘플을 수신한다고 가정합니다. 샘플을 동기적으로 처리하는 경우 Receive 메서드 내에서 참조 수를 증가시키지 않습니다. Receive가 반환되면 출력 핀이 샘플을 해제하고 참조 수가 0으로 이동하고 샘플이 할당자의 풀로 돌아갑니다. 반면 입력 핀이 작업자 스레드에서 샘플을 처리하는 경우 Receive 메서드를 떠나기 전에 참조 수가 증가합니다. 이제 참조 수가 2입니다. 출력 핀이 샘플을 해제하면 개수는 1로 이동합니다. 샘플은 아직 풀로 돌아가지 않습니다. 작업자 스레드가 샘플로 완료되면 Release 를 호출하여 샘플을 해제합니다. 이제 샘플이 풀로 돌아갑니다.

핀이 샘플을 받으면 데이터를 다른 샘플에 복사하거나 원래 샘플을 수정하고 다음 필터에 전달할 수 있습니다. 잠재적으로 샘플은 그래프의 전체 길이를 이동할 수 있으며, 각 필터 는 AddRefRelease 를 차례로 호출합니다. 따라서 출력 핀은 수신을 호출한 후 샘플을 다시 사용하면 안 됩니다. 다운스트림 필터가 샘플을 사용할 수 있기 때문입니다. 출력 핀은 항상 GetBuffer 를 호출하여 새 샘플을 가져와야 합니다.

이 메커니즘은 필터가 동일한 버퍼를 다시 사용하기 때문에 메모리 할당의 양을 줄입니다. 또한 할당자가 사용 가능한 샘플 목록을 유지 관리하므로 처리되지 않은 데이터에 대해 필터가 실수로 작성되지 않도록 방지합니다.

필터는 입력 및 출력에 별도의 할당자를 사용할 수 있습니다. 입력 데이터를 확장하는 경우(예: 압축 해제) 이 작업을 수행할 수 있습니다. 출력이 입력보다 크지 않으면 필터가 데이터를 새 샘플에 복사하지 않고 처리할 수 있습니다. 이 경우 둘 이상의 핀 연결이 하나의 할당자를 공유할 수 있습니다.

할당자 커밋 및 커밋 해제

필터가 할당자를 처음 만들 때 할당자는 메모리 버퍼를 예약하지 않았습니다. 이 시점에서 GetBuffer 메서드에 대한 모든 호출은 실패합니다. 스트리밍이 시작되면 출력 핀은 할당자를 커밋하여 메모리를 할당하는 IMemAllocator::Commit을 호출합니다. 이제 핀에서 GetBuffer를 호출할 수 있습니다.

스트리밍이 중지되면 핀은 할당자를 커밋 해제하는 IMemAllocator::D ecommit을 호출합니다. GetBuffer에 대한 모든 후속 호출은 할당자가 다시 커밋될 때까지 실패합니다. 또한 GetBuffer 에 대한 호출이 현재 샘플을 기다리는 동안 차단된 경우 오류 코드를 즉시 반환합니다. Decommit 메서드는 구현에 따라 메모리를 해제하거나 해제하지 않을 수 있습니다. 예를 들어 CMemAllocator 클래스는 소멸자 메서드가 메모리를 해제할 때까지 기다립니다.

필터 그래프의 Data Flow