streamconfig.cpp 9.1 KB
/* 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;
}