static inline bool MapPacketIDs(IBaseFilter *demuxer, ULONG video, ULONG audio)
{
	CComPtr<IPin> videoPin, audioPin;
	HRESULT       hr;

	if (!GetPinByName(demuxer, PINDIR_OUTPUT, DEMUX_VIDEO_PIN, &videoPin)) {
		Warning(L"Encoded Device: Could not get video pin from "
		        L"demuxer");
		return false;
	}

	if (!GetPinByName(demuxer, PINDIR_OUTPUT, DEMUX_AUDIO_PIN, &audioPin)) {
		Warning(L"Encoded Device: Could not get audio pin from "
		        L"demuxer");
		return false;
	}

	hr = MapPinToPacketID(videoPin, video);
	if (FAILED(hr)) {
		WarningHR(L"Encoded Device: Failed to map demuxer video pin "
		          L"packet ID", hr);
		return false;
	}

	hr = MapPinToPacketID(audioPin, audio);
	if (FAILED(hr)) {
		WarningHR(L"Encoded Device: Failed to map demuxer audio pin "
		          L"packet ID", hr);
		return false;
	}

	return true;
}
bool HVideoEncoder::ConnectFilters()
{
	ComPtr<IPin> deviceIn;
	ComPtr<IPin> deviceOut;
	ComPtr<IPin> encoderIn;
	ComPtr<IPin> encoderOut;
	bool success;
	HRESULT hr;

	success = GetPinByName(device, PINDIR_INPUT, L"YUV In", &deviceIn);
	if (!success) {
		Warning(L"Failed to get YUV In pin");
		return false;
	}

	success = GetPinByName(device, PINDIR_OUTPUT, L"Virtual Video Out",
			&deviceOut);
	if (!success) {
		Warning(L"Failed to get Virtual Video Out pin");
		return false;
	}

	success = GetPinByName(encoder, PINDIR_INPUT, L"Virtual Video In",
			&encoderIn);
	if (!success) {
		Warning(L"Failed to get encoder input pin");
		return false;
	}

	success = GetPinByName(encoder, PINDIR_OUTPUT, nullptr, &encoderOut);
	if (!success) {
		Warning(L"Failed to get encoder output pin");
		return false;
	}

	hr = graph->ConnectDirect(output->GetPin(), deviceIn, nullptr);
	if (FAILED(hr)) {
		WarningHR(L"Failed to connect output to device", hr);
		return false;
	}

	hr = graph->ConnectDirect(deviceOut, encoderIn, nullptr);
	if (FAILED(hr)) {
		WarningHR(L"Failed to connect device to encoder", hr);
		return false;
	}

	hr = graph->ConnectDirect(encoderOut, capture->GetPin(), nullptr);
	if (FAILED(hr)) {
		WarningHR(L"Failed to connect encoder to capture", hr);
		return false;
	}

	return true;
}
bool HDevice::SetupExceptionVideoCapture(IBaseFilter *filter,
		VideoConfig &config)
{
	ComPtr<IPin> pin;

	if (GetPinByName(filter, PINDIR_OUTPUT, L"656", &pin))
		return SetupEncodedVideoCapture(filter, config, HD_PVR2);

	else if (GetPinByName(filter, PINDIR_OUTPUT, L"TS Out", &pin))
		return SetupEncodedVideoCapture(filter, config, Roxio);

	return false;
}
static void EnumExceptionVideoDevice(std::vector<VideoDevice> &devices,
		IBaseFilter *filter,
		const wchar_t *deviceName,
		const wchar_t *devicePath)
{
	ComPtr<IPin> pin;

	if (GetPinByName(filter, PINDIR_OUTPUT, L"656", &pin))
		EnumEncodedVideo(devices, deviceName, devicePath, HD_PVR2);

	else if (GetPinByName(filter, PINDIR_OUTPUT, L"TS Out", &pin))
		EnumEncodedVideo(devices, deviceName, devicePath, Roxio);
}
bool HVideoEncoder::SetupCrossbar()
{
	ComPtr<IBaseFilter> crossbar;
	ComPtr<IPin> pin;
	REGPINMEDIUM medium;

	/* C353 has no crossbar */
	if (config.name.find(L"C353") != std::string::npos)
		return true;

	if (!GetPinByName(device, PINDIR_INPUT, L"Analog Video In", &pin)) {
		Warning(L"Failed to get Analog Video In pin");
		return false;
	}
	if (!GetPinMedium(pin, medium)) {
		Warning(L"Failed to get Analog Video In pin medium");
		return false;
	}
	if (!GetFilterByMedium(AM_KSCATEGORY_CROSSBAR, medium, &crossbar)) {
		Warning(L"Failed to get crossbar filter");
		return false;
	}

	graph->AddFilter(crossbar, L"Crossbar Filter");

	if (!DirectConnectFilters(graph, crossbar, device)) {
		Warning(L"Failed to connect crossbar to device");
		return false;
	}

	return true;
}
static inline bool CreateFilters(IBaseFilter *filter,
		IBaseFilter **crossbar, IBaseFilter **encoder,
		IBaseFilter **demuxer)
{
	CComPtr<IPin> inputPin;
	CComPtr<IPin> outputPin;
	REGPINMEDIUM  inMedium;
	REGPINMEDIUM  outMedium;
	bool          hasOutMedium;
	HRESULT       hr;

	if (!GetPinByName(filter, PINDIR_INPUT, nullptr, &inputPin)) {
		Warning(L"Encoded Device: Failed to get input pin");
		return false;
	}

	if (!GetPinByName(filter, PINDIR_OUTPUT, nullptr, &outputPin)) {
		Warning(L"Encoded Device: Failed to get output pin");
		return false;
	}

	if (!GetPinMedium(inputPin, inMedium)) {
		Warning(L"Encoded Device: Failed to get input pin medium");
		return false;
	}

	hasOutMedium = GetPinMedium(outputPin, outMedium);

	if (!GetFilterByMedium(AM_KSCATEGORY_CROSSBAR, inMedium, crossbar)) {
		Warning(L"Encoded Device: Failed to get crossbar filter");
		return false;
	}

	/* perfectly okay if there's no encoder filter, some don't have them */
	if (hasOutMedium)
		GetFilterByMedium(KSCATEGORY_ENCODER, outMedium, encoder);

	hr = CoCreateInstance(CLSID_MPEG2Demultiplexer, nullptr,
			CLSCTX_INPROC_SERVER, IID_IBaseFilter, (void**)demuxer);
	if (FAILED(hr)) {
		WarningHR(L"Encoded Device: Failed to create demuxer", hr);
		return false;
	}

	return true;
}
bool HDevice::FindCrossbar(IBaseFilter *filter, IBaseFilter **crossbar)
{
	ComPtr<IPin> pin;
	REGPINMEDIUM medium;
	HRESULT hr;

	hr = builder->FindInterface(NULL, NULL, filter, IID_IAMCrossbar,
			(void**)crossbar);
	if (SUCCEEDED(hr))
		return true;

	if (!GetPinByName(filter, PINDIR_INPUT, nullptr, &pin))
		return false;
	if (!GetPinMedium(pin, medium))
		return false;
	if (!GetFilterByMedium(AM_KSCATEGORY_CROSSBAR, medium, crossbar))
		return false;

	graph->AddFilter(*crossbar, L"Crossbar Filter");
	return true;
}
bool HDevice::ConnectPins(const GUID &category, const GUID &type,
		IBaseFilter *filter, IBaseFilter *capture)
{
	HRESULT hr;
	ComPtr<IBaseFilter> crossbar;
	ComPtr<IPin> filterPin;
	ComPtr<IPin> capturePin;
	bool connectCrossbar = !encodedDevice && type == MEDIATYPE_Video;

	if (!EnsureInitialized(L"HDevice::ConnectPins") ||
	    !EnsureInactive(L"HDevice::ConnectPins"))
		return false;

	if (connectCrossbar && FindCrossbar(filter, &crossbar)) {
		if (!DirectConnectFilters(graph, crossbar, filter)) {
			Warning(L"HDevice::ConnectPins: Failed to connect "
			        L"crossbar");
			return false;
		}
	}

	if (!GetFilterPin(filter, type, category, PINDIR_OUTPUT, &filterPin)) {
		Error(L"HDevice::ConnectPins: Failed to find pin");
		return false;
	}

	if (!GetPinByName(capture, PINDIR_INPUT, nullptr, &capturePin)) {
		Error(L"HDevice::ConnectPins: Failed to find capture pin");
		return false;
	}

	hr = graph->ConnectDirect(filterPin, capturePin, nullptr);
	if (FAILED(hr)) {
		WarningHR(L"HDevice::ConnectPins: failed to connect pins",
				hr);
		return false;
	}

	return true;
}
bool HVideoEncoder::SetupEncoder(IBaseFilter *filter)
{
	ComPtr<IBaseFilter>            deviceFilter;
	ComPtr<IPin>                   inputPin;
	ComPtr<IPin>                   outputPin;
	REGPINMEDIUM                   medium;
	MediaTypePtr                   mtRaw;
	MediaTypePtr                   mtEncoded;

	if (!GetPinByName(filter, PINDIR_INPUT, nullptr, &inputPin)) {
		Warning(L"Could not get encoder input pin");
		return false;
	}
	if (!GetPinByName(filter, PINDIR_OUTPUT, nullptr, &outputPin)) {
		Warning(L"Could not get encoder output pin");
		return false;
	}
	if (!GetPinMedium(inputPin, medium)) {
		Warning(L"Could not get input pin medium");
		return false;
	}

	inputPin.Release();

	if (!GetFilterByMedium(CLSID_VideoInputDeviceCategory, medium,
				&deviceFilter)) {
		Warning(L"Could not get device filter from medium");
		return false;
	}
	if (!GetPinByName(deviceFilter, PINDIR_INPUT, L"YUV In", &inputPin)) {
		Warning(L"Could not device YUV pin");
		return false;
	}
	if (!GetPinFirstMediaType(inputPin, &mtRaw)) {
		Warning(L"Could not get YUV pin media type");
		return false;
	}
	if (!GetPinFirstMediaType(outputPin, &mtEncoded)) {
		Warning(L"Could not get encoder output pin media type");
		return false;
	}

	PinCaptureInfo captureInfo;
	captureInfo.callback           = [this] (IMediaSample *s) {Receive(s);};
	captureInfo.expectedMajorType  = mtEncoded->majortype;
	captureInfo.expectedSubType    = mtEncoded->subtype;

	PinOutputInfo outputInfo;
	outputInfo.expectedMajorType   = mtRaw->majortype;
	outputInfo.expectedSubType     = mtRaw->subtype;
	outputInfo.cx                  = config.cx;
	outputInfo.cy                  = config.cy;

	InitializeVideoFormat(outputInfo.mt);

	encoder = filter;
	device  = deviceFilter;
	capture = new CaptureFilter(captureInfo);
	output  = new OutputFilter(outputInfo);

	graph->AddFilter(output, nullptr);
	graph->AddFilter(device, L"Device Filter");
	graph->AddFilter(encoder, L"Encoder Filter");
	graph->AddFilter(capture, nullptr);
	return true;
}