akvirtualcamera/dshow/VirtualCamera/src/streamconfig.cpp
Gonzalo Exequiel Pedone 84d630e422
Windows plugin working, 'update' command still needs elevated privileges
but all other commands can be run as normal user.
2020-10-08 20:07:51 -03:00

265 lines
9.1 KiB
C++

/* akvirtualcamera, virtual camera for Mac and Windows.
* Copyright (C) 2020 Gonzalo Exequiel Pedone
*
* akvirtualcamera is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* akvirtualcamera is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with akvirtualcamera. If not, see <http://www.gnu.org/licenses/>.
*
* Web-Site: http://webcamoid.github.io/
*/
#include <strmif.h>
#include <amvideo.h>
#include <dvdmedia.h>
#include <uuids.h>
#include <dshow.h>
#include "streamconfig.h"
#include "basefilter.h"
#include "pin.h"
#include "PlatformUtils/src/utils.h"
#include "VCamUtils/src/utils.h"
namespace AkVCam
{
class StreamConfigPrivate
{
public:
Pin *m_pin {nullptr};
AM_MEDIA_TYPE *m_mediaType {nullptr};
};
}
AkVCam::StreamConfig::StreamConfig(Pin *pin):
CUnknown(this, IID_IAMStreamConfig)
{
this->d = new StreamConfigPrivate;
this->d->m_pin = pin;
}
AkVCam::StreamConfig::~StreamConfig()
{
deleteMediaType(&this->d->m_mediaType);
delete this->d;
}
void AkVCam::StreamConfig::setPin(Pin *pin)
{
this->d->m_pin = pin;
}
HRESULT AkVCam::StreamConfig::SetFormat(AM_MEDIA_TYPE *pmt)
{
AkLogFunction();
if (!pmt)
return E_POINTER;
if (this->d->m_pin) {
PIN_INFO pinInfo;
if (SUCCEEDED(this->d->m_pin->QueryPinInfo(&pinInfo))
&& pinInfo.pFilter) {
FILTER_STATE state;
auto result = pinInfo.pFilter->GetState(0, &state);
pinInfo.pFilter->Release();
if (FAILED(result) || state != State_Stopped)
return VFW_E_NOT_STOPPED;
}
if (this->d->m_pin->QueryAccept(pmt) != S_OK)
return VFW_E_INVALIDMEDIATYPE;
}
deleteMediaType(&this->d->m_mediaType);
this->d->m_mediaType = createMediaType(pmt);
IPin *pin = nullptr;
if (SUCCEEDED(this->d->m_pin->ConnectedTo(&pin))) {
if (this->d->m_pin
&& this->d->m_pin->baseFilter()
&& this->d->m_pin->baseFilter()->filterGraph())
this->d->m_pin->baseFilter()->filterGraph()->Reconnect(this->d->m_pin);
pin->Release();
}
return S_OK;
}
HRESULT AkVCam::StreamConfig::GetFormat(AM_MEDIA_TYPE **pmt)
{
AkLogFunction();
if (!pmt)
return E_POINTER;
*pmt = nullptr;
if (this->d->m_mediaType) {
*pmt = createMediaType(this->d->m_mediaType);
} else {
IEnumMediaTypes *mediaTypes = nullptr;
if (FAILED(this->d->m_pin->EnumMediaTypes(&mediaTypes)))
return E_FAIL;
AM_MEDIA_TYPE *mediaType = nullptr;
mediaTypes->Reset();
if (mediaTypes->Next(1, &mediaType, nullptr) == S_OK)
*pmt = mediaType;
mediaTypes->Release();
}
AkLogInfo() << "MediaType: " << stringFromMediaType(*pmt) << std::endl;
return *pmt? S_OK: E_FAIL;
}
HRESULT AkVCam::StreamConfig::GetNumberOfCapabilities(int *piCount,
int *piSize)
{
AkLogFunction();
if (!piCount || !piSize)
return E_POINTER;
*piCount = 0;
*piSize = 0;
if (this->d->m_pin) {
IEnumMediaTypes *mediaTypes = nullptr;
if (SUCCEEDED(this->d->m_pin->EnumMediaTypes(&mediaTypes))) {
mediaTypes->Reset();
AM_MEDIA_TYPE *mediaType = nullptr;
while (mediaTypes->Next(1, &mediaType, nullptr) == S_OK) {
(*piCount)++;
deleteMediaType(&mediaType);
}
mediaTypes->Release();
}
}
if (*piCount)
*piSize = sizeof(VIDEO_STREAM_CONFIG_CAPS);
return S_OK;
}
HRESULT AkVCam::StreamConfig::GetStreamCaps(int iIndex,
AM_MEDIA_TYPE **pmt,
BYTE *pSCC)
{
AkLogFunction();
if (!pmt || !pSCC)
return E_POINTER;
*pmt = nullptr;
auto configCaps = reinterpret_cast<VIDEO_STREAM_CONFIG_CAPS *>(pSCC);
memset(configCaps, 0, sizeof(VIDEO_STREAM_CONFIG_CAPS));
if (iIndex < 0)
return E_INVALIDARG;
if (this->d->m_pin) {
IEnumMediaTypes *mediaTypes = nullptr;
if (SUCCEEDED(this->d->m_pin->EnumMediaTypes(&mediaTypes))) {
mediaTypes->Reset();
AM_MEDIA_TYPE *mediaType = nullptr;
for (int i = 0;
mediaTypes->Next(1, &mediaType, nullptr) == S_OK;
i++) {
if (i == iIndex) {
*pmt = mediaType;
if (IsEqualGUID(mediaType->formattype, FORMAT_VideoInfo)) {
auto format = reinterpret_cast<VIDEOINFOHEADER *>(mediaType->pbFormat);
configCaps->guid = mediaType->formattype;
configCaps->VideoStandard = AnalogVideo_None;
configCaps->InputSize.cx = format->bmiHeader.biWidth;
configCaps->InputSize.cy = format->bmiHeader.biHeight;
configCaps->MinCroppingSize.cx = format->bmiHeader.biWidth;
configCaps->MinCroppingSize.cy = format->bmiHeader.biHeight;
configCaps->MaxCroppingSize.cx = format->bmiHeader.biWidth;
configCaps->MaxCroppingSize.cy = format->bmiHeader.biHeight;
configCaps->CropGranularityX = 1;
configCaps->CropGranularityY = 1;
configCaps->CropAlignX = 0;
configCaps->CropAlignY = 0;
configCaps->MinOutputSize.cx = format->bmiHeader.biWidth;
configCaps->MinOutputSize.cy = format->bmiHeader.biHeight;
configCaps->MaxOutputSize.cx = format->bmiHeader.biWidth;
configCaps->MaxOutputSize.cy = format->bmiHeader.biHeight;
configCaps->OutputGranularityX = 1;
configCaps->OutputGranularityY = 1;
configCaps->StretchTapsX = 1;
configCaps->StretchTapsY = 1;
configCaps->ShrinkTapsX = 1;
configCaps->ShrinkTapsY = 1;
configCaps->MinFrameInterval = format->AvgTimePerFrame;
configCaps->MaxFrameInterval = format->AvgTimePerFrame;
configCaps->MinBitsPerSecond = LONG(format->dwBitRate);
configCaps->MaxBitsPerSecond = LONG(format->dwBitRate);
} else if (IsEqualGUID(mediaType->formattype, FORMAT_VideoInfo2)) {
auto format = reinterpret_cast<VIDEOINFOHEADER2 *>(mediaType->pbFormat);
configCaps->guid = mediaType->formattype;
configCaps->VideoStandard = AnalogVideo_None;
configCaps->InputSize.cx = format->bmiHeader.biWidth;
configCaps->InputSize.cy = format->bmiHeader.biHeight;
configCaps->MinCroppingSize.cx = format->bmiHeader.biWidth;
configCaps->MinCroppingSize.cy = format->bmiHeader.biHeight;
configCaps->MaxCroppingSize.cx = format->bmiHeader.biWidth;
configCaps->MaxCroppingSize.cy = format->bmiHeader.biHeight;
configCaps->CropGranularityX = 1;
configCaps->CropGranularityY = 1;
configCaps->CropAlignX = 0;
configCaps->CropAlignY = 0;
configCaps->MinOutputSize.cx = format->bmiHeader.biWidth;
configCaps->MinOutputSize.cy = format->bmiHeader.biHeight;
configCaps->MaxOutputSize.cx = format->bmiHeader.biWidth;
configCaps->MaxOutputSize.cy = format->bmiHeader.biHeight;
configCaps->OutputGranularityX = 1;
configCaps->OutputGranularityY = 1;
configCaps->StretchTapsX = 1;
configCaps->StretchTapsY = 1;
configCaps->ShrinkTapsX = 1;
configCaps->ShrinkTapsY = 1;
configCaps->MinFrameInterval = format->AvgTimePerFrame;
configCaps->MaxFrameInterval = format->AvgTimePerFrame;
configCaps->MinBitsPerSecond = LONG(format->dwBitRate);
configCaps->MaxBitsPerSecond = LONG(format->dwBitRate);
}
break;
}
deleteMediaType(&mediaType);
}
mediaTypes->Release();
}
}
AkLogInfo() << "Media Type: " << stringFromMediaType(*pmt) << std::endl;
return *pmt? S_OK: S_FALSE;
}