// 指定フィルタ経由してピン接続を行う(主に入力=1/出力=1の経由型フィルタ接続用) // Filter指定版 // // lpwszFilterName は NULL でも良い。 // ppCurrentOutputPin が上流に接続された有効なピンであるのが前提。 // ppCurrentOutputPin は正常終了なら解放され、フィルタの出力ピンが*ppNewOutputPinになる。 // ppNewOutputPin==NULL の場合、フィルタの出力ピンが*ppCurrentOutputPinになる。元の*ppCurrentOutputPinは解放される // HRESULT DirectShowUtil::AppendFilterAndConnect(IGraphBuilder *pFilterGraph, IBaseFilter *pFilter,LPCWSTR lpwszFilterName, IPin **ppCurrentOutputPin,IPin **ppNewOutputPin,bool fDirect) { HRESULT hr; // ポインタチェック if (!pFilterGraph || !pFilter || !ppCurrentOutputPin) return E_INVALIDARG; // 同じピンアドレスなら New==NULL で入力されたのと同義 if (ppCurrentOutputPin==ppNewOutputPin) ppNewOutputPin = NULL; if (!lpwszFilterName) lpwszFilterName = L"No Name"; hr = pFilterGraph->AddFilter(pFilter, lpwszFilterName); if (FAILED(hr)) { // フィルタに追加失敗 return hr; } IPin *pInput = GetFilterPin(pFilter, PINDIR_INPUT); if (!pInput) { // 入力ピンが見つからない pFilterGraph->RemoveFilter(pFilter); return E_FAIL; } // 接続 if (fDirect) hr = pFilterGraph->ConnectDirect(*ppCurrentOutputPin,pInput,NULL); else hr = pFilterGraph->Connect(*ppCurrentOutputPin,pInput); if (FAILED(hr)) { // 接続できない SAFE_RELEASE(pInput); pFilterGraph->RemoveFilter(pFilter); return hr; } // 接続に使ったピン解放 SAFE_RELEASE(*ppCurrentOutputPin); SAFE_RELEASE(pInput); // 次フィルタへの出力ピンを探す(出力ピンが無くても処理は続行) IPin *pNewOutput = GetFilterPin(pFilter, PINDIR_OUTPUT); if (ppNewOutputPin) { // 新出力ピンに出力 // 元の出力ピンは既に解放済み *ppNewOutputPin = pNewOutput; } else { // 出力ピンを更新(ppCurrentOutputPin==ppNewOutputPinの場合でも実体が上書きだから更新となる) // 元の出力ピンは既に解放済み *ppCurrentOutputPin = pNewOutput; } #ifdef _DEBUG LONG refCount = GetRefCount(pFilter); #endif return S_OK; }
static bool EnumVideoDevice(std::vector<VideoDevice> &devices, IBaseFilter *filter, const wchar_t *deviceName, const wchar_t *devicePath) { ComPtr<IPin> pin; ComPtr<IPin> audioPin; ComPtr<IBaseFilter> audioFilter; VideoDevice info; if (wcsstr(deviceName, L"C875") != nullptr || wcsstr(deviceName, L"Prif Streambox") != nullptr || wcsstr(deviceName, L"C835") != nullptr) { EnumEncodedVideo(devices, deviceName, devicePath, AV_LGP); return true; } else if (wcsstr(deviceName, L"Hauppauge HD PVR Capture") != nullptr) { EnumEncodedVideo(devices, deviceName, devicePath, HD_PVR1); return true; } bool success = GetFilterPin(filter, MEDIATYPE_Video, PIN_CATEGORY_CAPTURE, PINDIR_OUTPUT, &pin); /* if this device has no standard capture pin, see if it's an * encoded device, and get its information if so (all encoded devices * are exception devices pretty much) */ if (!success) { EnumExceptionVideoDevice(devices, filter, deviceName, devicePath); return true; } if (!EnumVideoCaps(pin, info.caps)) return true; info.audioAttached = GetFilterPin(filter, MEDIATYPE_Audio, PIN_CATEGORY_CAPTURE, PINDIR_OUTPUT, &audioPin); // Fallback: Find a corresponding audio filter for the same device if (!info.audioAttached) { info.separateAudioFilter = GetDeviceAudioFilter(devicePath, &audioFilter); info.audioAttached = info.separateAudioFilter; } info.name = deviceName; if (devicePath) info.path = devicePath; devices.push_back(info); return true; }
bool HDevice::SetupAudioCapture(IBaseFilter *filter, AudioConfig &config) { ComPtr<IPin> pin; MediaTypePtr defaultMT; bool success; HRESULT hr; success = GetFilterPin(filter, MEDIATYPE_Audio, PIN_CATEGORY_CAPTURE, PINDIR_OUTPUT, &pin); if (!success) { Error(L"Could not get audio pin"); return false; } ComQIPtr<IAMStreamConfig> pinConfig(pin); if (config.useDefaultConfig) { MediaTypePtr defaultMT; if (pinConfig && SUCCEEDED(pinConfig->GetFormat(&defaultMT))) { audioMediaType = defaultMT; } else { if (!SetupExceptionAudioCapture(pin)) { Error(L"Could not get default format for " L"audio pin"); return false; } } } else { if (!GetClosestAudioMediaType(filter, config, audioMediaType)) { Error(L"Could not get closest audio media type"); return false; } } if (!!pinConfig) { hr = pinConfig->SetFormat(audioMediaType); if (FAILED(hr) && hr != E_NOTIMPL) { Error(L"Could not set audio format"); return false; } } ConvertAudioSettings(); PinCaptureInfo info; info.callback = [this] (IMediaSample *s) {Receive(false, s);}; info.expectedMajorType = audioMediaType->majortype; info.expectedSubType = audioMediaType->subtype; audioCapture = new CaptureFilter(info); audioFilter = filter; audioConfig = config; graph->AddFilter(audioCapture, L"Audio Capture Filter"); if (!config.useVideoDevice) graph->AddFilter(audioFilter, L"Audio 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; }
static bool EnumAudioDevice(vector<AudioDevice> &devices, IBaseFilter *filter, const wchar_t *deviceName, const wchar_t *devicePath) { ComPtr<IPin> pin; AudioDevice info; bool success = GetFilterPin(filter, MEDIATYPE_Audio, PIN_CATEGORY_CAPTURE, PINDIR_OUTPUT, &pin); if (!success) return true; if (!EnumAudioCaps(pin, info.caps)) return true; info.name = deviceName; if (devicePath) info.path = devicePath; devices.push_back(info); return true; }
bool GetClosestAudioMediaType(IBaseFilter *filter, AudioConfig &config, MediaType &mt) { CComPtr<IPin> pin; ClosestAudioData data(config, mt); bool success; success = GetFilterPin(filter, MEDIATYPE_Audio, PIN_CATEGORY_CAPTURE, PINDIR_OUTPUT, &pin); if (!success || pin == NULL) { Error(L"GetClosestAudioMediaType: Could not get pin"); return false; } success = EnumPinCaps(pin, EnumCapsCallback(ClosestAudioMTCallback), &data); if (!success) { Error(L"GetClosestAudioMediaType: Could not enumerate caps"); return false; } return data.found; }
void HDevice::SetAudioBuffering(int bufferingMs) { ComPtr<IPin> pin; bool success = GetFilterPin(audioFilter, MEDIATYPE_Audio, PIN_CATEGORY_CAPTURE, PINDIR_OUTPUT, &pin); if (!success) return; ComQIPtr<IAMStreamConfig> config(pin); if (!config) return; ComQIPtr<IAMBufferNegotiation> neg(pin); if (!neg) return; MediaTypePtr mt; if (FAILED(config->GetFormat(&mt))) return; if (mt->formattype != FORMAT_WaveFormatEx) return; if (mt->cbFormat != sizeof(WAVEFORMATEX)) return; WAVEFORMATEX *wfex = (WAVEFORMATEX*)mt->pbFormat; ALLOCATOR_PROPERTIES props; props.cBuffers = -1; props.cbBuffer = wfex->nAvgBytesPerSec * bufferingMs / 1000; props.cbAlign = -1; props.cbPrefix = -1; HRESULT hr = neg->SuggestAllocatorProperties(&props); if (FAILED(hr)) WarningHR(L"Could not set allocator properties on audio " L"capture pin", hr); }
bool HDevice::SetupVideoCapture(IBaseFilter *filter, VideoConfig &config) { ComPtr<IPin> pin; HRESULT hr; bool success; if (config.name.find(L"C875") != std::string::npos || config.name.find(L"C835") != std::string::npos) return SetupEncodedVideoCapture(filter, config, AV_LGP); else if (config.name.find(L"IT9910") != std::string::npos) return SetupEncodedVideoCapture(filter, config, HD_PVR_Rocket); else if (config.name.find(HD_PVR1_NAME) != std::string::npos) return SetupEncodedVideoCapture(filter, config, HD_PVR1); success = GetFilterPin(filter, MEDIATYPE_Video, PIN_CATEGORY_CAPTURE, PINDIR_OUTPUT, &pin); if (!success) { if (SetupExceptionVideoCapture(filter, config)) { return true; } else { Error(L"Could not get video pin"); return false; } } ComQIPtr<IAMStreamConfig> pinConfig(pin); if (pinConfig == NULL) { Error(L"Could not get IAMStreamConfig for device"); return false; } if (config.useDefaultConfig) { MediaTypePtr defaultMT; hr = pinConfig->GetFormat(&defaultMT); if (hr == E_NOTIMPL) { if (!GetPinMediaType(pin, videoMediaType)) { Error(L"Couldn't get pin media type"); return false; } } else if (FAILED(hr)) { ErrorHR(L"Could not get default format for video", hr); return false; } else { videoMediaType = defaultMT; } ConvertVideoSettings(); config.format = config.internalFormat = VideoFormat::Any; } if (!GetClosestVideoMediaType(filter, config, videoMediaType)) { Error(L"Could not get closest video media type"); return false; } hr = pinConfig->SetFormat(videoMediaType); if (FAILED(hr) && hr != E_NOTIMPL) { ErrorHR(L"Could not set video format", hr); return false; } ConvertVideoSettings(); PinCaptureInfo info; info.callback = [this] (IMediaSample *s) {Receive(true, s);}; info.expectedMajorType = videoMediaType->majortype; /* attempt to force intermediary filters for these types */ if (videoConfig.format == VideoFormat::XRGB) info.expectedSubType = MEDIASUBTYPE_RGB32; else if (videoConfig.format == VideoFormat::ARGB) info.expectedSubType = MEDIASUBTYPE_ARGB32; else if (videoConfig.format == VideoFormat::YVYU) info.expectedSubType = MEDIASUBTYPE_YVYU; else if (videoConfig.format == VideoFormat::YUY2) info.expectedSubType = MEDIASUBTYPE_YUY2; else if (videoConfig.format == VideoFormat::UYVY) info.expectedSubType = MEDIASUBTYPE_UYVY; else info.expectedSubType = videoMediaType->subtype; videoCapture = new CaptureFilter(info); videoFilter = filter; graph->AddFilter(videoCapture, L"Video Capture Filter"); graph->AddFilter(videoFilter, L"Video Filter"); return true; }
// Mpegデコーダを追加してピン接続を行う。 // pUtilに整列済みデコーダ列挙を入れておくとその整列順に接続が行われる。NULLならデフォルト順。 // わざわざ指定する理由 : このクラスに検索されたフィルタがMpeg2のものとは限らない為。 // // ppMpeg2DecoderFilter は接続に使われたMpeg2インターフェースを受けとる。 // ppCurrentOutputPin が上流に接続された有効なピンであるのが前提。 // ppCurrentOutputPin は正常終了なら解放され、デコーダフィルタの出力ピンが*ppNewOutputPinになる。 // ppNewOutputPin==NULL の場合、デコーダフィルタの出力ピンが*ppCurrentOutputPinになる。元の*ppCurrentOutputPinは解放される // // 失敗した場合でもフィルタ解放は行われないパスがある(既に接続済みの場合)。戻りフィルタは確認して解放すること bool DirectShowUtil::AppendMpeg2Decoder_and_Connect(IGraphBuilder *pFilterGraph, CDirectShowFilterFinder *pUtil, IBaseFilter **ppMpeg2DecoderFilter,wchar_t *lpszDecoderName,int iDecNameBufLen, IPin **ppCurrentOutputPin, IPin **ppNewOutputPin) { HRESULT hr; IPin *pInput = NULL; CDirectShowFilterFinder cUtil; CLSID guidFilter = CLSID_NULL; bool bRet; wchar_t tmp[128]; if(!lpszDecoderName){ lpszDecoderName = tmp; iDecNameBufLen = 128; } // ポインタチェック if(!pFilterGraph || !ppMpeg2DecoderFilter || !ppCurrentOutputPin) return false; // 同じピンアドレスなら New==NULL で入力されたのと同義 if(ppCurrentOutputPin==ppNewOutputPin) ppNewOutputPin = NULL; // 指定がない場合のフィルタ検索 if(!pUtil){ pUtil = &cUtil; if(!pUtil->FindFilter(&MEDIATYPE_Video,&MEDIASUBTYPE_MPEG2_VIDEO)) return false; } // 戻り値をクリア *ppMpeg2DecoderFilter = NULL; bRet=false; for(int i=0;!bRet&&i<pUtil->GetFilterCount();i++){ if(!pUtil->GetFilterInfo(i,&guidFilter,lpszDecoderName,iDecNameBufLen)) continue; hr = ::CoCreateInstance(guidFilter, NULL, CLSCTX_INPROC_SERVER, IID_IBaseFilter, (void**)ppMpeg2DecoderFilter); if(FAILED(hr)) continue; hr = pFilterGraph->AddFilter(*ppMpeg2DecoderFilter,lpszDecoderName); if(FAILED(hr)){ SAFE_RELEASE(*ppMpeg2DecoderFilter); continue; } pInput = CDirectShowFilterFinder::GetFilterPin(*ppMpeg2DecoderFilter,PINDIR_INPUT); if(!pInput){ SAFE_RELEASE(*ppMpeg2DecoderFilter); continue; } hr = pFilterGraph->Connect(*ppCurrentOutputPin,pInput); if(FAILED(hr)){ SAFE_RELEASE(pInput); pFilterGraph->RemoveFilter(*ppMpeg2DecoderFilter); SAFE_RELEASE(*ppMpeg2DecoderFilter); continue; } else { bRet=true; } } if(!bRet) { // 全組み合わせで適合デコーダが無かった return false; } // 接続に使ったピン解放 SAFE_RELEASE(*ppCurrentOutputPin); SAFE_RELEASE(pInput); // 次フィルタへの出力ピンを探す IPin *pNewOutput = GetFilterPin(*ppMpeg2DecoderFilter, PINDIR_OUTPUT); if(!pNewOutput){ // 出力ピンが見つからない return false; } if(ppNewOutputPin){ // 新出力ピンに出力 // 元の出力ピンは既に解放済み *ppNewOutputPin = pNewOutput; }else{ // 出力ピンを更新(ppCurrentOutputPin==ppNewOutputPinの場合でも実体が上書きだから更新となる) // 元の出力ピンは既に解放済み *ppCurrentOutputPin = pNewOutput; } return true; }