summaryrefslogtreecommitdiffstats
path: root/src/detection/camera/camera_windows.cpp
blob: 1f423069620b14d42ae38de5b418a87ae645636c (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
extern "C" {
#include "camera.h"
#include "common/library.h"
#include "common/windows/com.h"
}
#include "common/windows/unicode.hpp"
#include "common/windows/util.hpp"

#include <initguid.h>
#include <mfapi.h>
#include <mfidl.h>

extern "C" const char* ffDetectCamera(FF_A_UNUSED FFlist* result) {
    FF_LIBRARY_LOAD_MESSAGE(mfplat, "mfplat" FF_LIBRARY_EXTENSION, 1)
    FF_LIBRARY_LOAD_SYMBOL_MESSAGE(mfplat, MFCreateAttributes)
    FF_LIBRARY_LOAD_MESSAGE(mf, "mf" FF_LIBRARY_EXTENSION, 1)
    FF_LIBRARY_LOAD_SYMBOL_MESSAGE(mf, MFEnumDeviceSources)

    const char* error = ffInitCom();
    if (error) {
        return error;
    }

    IMFAttributes* FF_AUTO_RELEASE_COM_OBJECT attrs = nullptr;
    if (FAILED(ffMFCreateAttributes(&attrs, 1))) {
        return "MFCreateAttributes() failed";
    }

    if (FAILED(attrs->SetGUID(
            MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE,
            MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_GUID))) {
        return "SetGUID(MF_*) failed";
    }

    IMFActivate** devices = NULL;
    uint32_t count;

    if (FAILED(ffMFEnumDeviceSources(attrs, &devices, &count))) {
        return "MFEnumDeviceSources() failed";
    }

    for (uint32_t i = 0; i < count; i++) {
        IMFActivate* FF_AUTO_RELEASE_COM_OBJECT device = devices[i];

        wchar_t buffer[256];
        uint32_t length = 0;
        if (FAILED(device->GetString(MF_DEVSOURCE_ATTRIBUTE_FRIENDLY_NAME, buffer, ARRAY_SIZE(buffer), &length)) || length == 0) {
            continue;
        }

        FFCameraResult* camera = FF_LIST_ADD(FFCameraResult, *result);
        ffStrbufInitNWS(&camera->name, length, buffer);
        ffStrbufInit(&camera->colorspace);
        ffStrbufInit(&camera->vendor);
        ffStrbufInit(&camera->id);
        camera->width = 0;
        camera->height = 0;

        if (SUCCEEDED(device->GetString(MF_DEVSOURCE_ATTRIBUTE_SOURCE_TYPE_VIDCAP_SYMBOLIC_LINK, buffer, ARRAY_SIZE(buffer), &length)) && length > 0) {
            ffStrbufSetNWS(&camera->id, length, buffer);
        }

        IMFMediaSource* FF_AUTO_RELEASE_COM_OBJECT source = nullptr;
        if (FAILED(device->ActivateObject(IID_PPV_ARGS(&source)))) {
            continue;
        }

        on_scope_exit destroySource([&] { source->Shutdown(); });

        IMFPresentationDescriptor* FF_AUTO_RELEASE_COM_OBJECT pd = nullptr;
        if (FAILED(source->CreatePresentationDescriptor(&pd))) {
            continue;
        }

        IMFStreamDescriptor* FF_AUTO_RELEASE_COM_OBJECT sd = nullptr;
        BOOL selected;
        if (FAILED(pd->GetStreamDescriptorByIndex(0, &selected, &sd))) {
            continue;
        }

        IMFMediaTypeHandler* FF_AUTO_RELEASE_COM_OBJECT handler = nullptr;
        if (FAILED(sd->GetMediaTypeHandler(&handler))) {
            continue;
        }

        DWORD mediaTypeCount;
        if (FAILED(handler->GetMediaTypeCount(&mediaTypeCount))) {
            continue;
        }

        // Assume first type is the maximum resolution
        IMFMediaType* type = NULL;
        for (DWORD idx = 0; SUCCEEDED(handler->GetMediaTypeByIndex(idx, &type)); ++idx) {
            on_scope_exit destroyType([=] { type->Release(); });

            GUID majorType;
            if (FAILED(type->GetMajorType(&majorType)) || majorType != MFMediaType_Video) {
                continue;
            }

            MFVideoPrimaries primaries;
            static_assert(sizeof(primaries) == sizeof(uint32_t), "");
            if (SUCCEEDED(type->GetUINT32(MF_MT_VIDEO_PRIMARIES, (uint32_t*) &primaries))) {
                switch (primaries) {
                    case MFVideoPrimaries_BT709:
                        ffStrbufSetStatic(&camera->colorspace, "sRGB");
                        break;
                    case MFVideoPrimaries_BT470_2_SysM:
                    case MFVideoPrimaries_BT470_2_SysBG:
                        ffStrbufSetStatic(&camera->colorspace, "NTSC");
                        break;
                    case MFVideoPrimaries_SMPTE170M:
                        ffStrbufSetStatic(&camera->colorspace, "SMPTE 170M");
                        break;
                    case MFVideoPrimaries_SMPTE240M:
                        ffStrbufSetStatic(&camera->colorspace, "SMPTE 240M");
                        break;
                    case MFVideoPrimaries_EBU3213:
                        ffStrbufSetStatic(&camera->colorspace, "EBU 3213");
                        break;
                    case MFVideoPrimaries_SMPTE_C:
                        ffStrbufSetStatic(&camera->colorspace, "SMPTE C");
                        break;
                    case MFVideoPrimaries_BT2020:
                        ffStrbufSetStatic(&camera->colorspace, "BT.2020");
                        break;
                    case MFVideoPrimaries_XYZ:
                        ffStrbufSetStatic(&camera->colorspace, "XYZ");
                        break;
                    case MFVideoPrimaries_DCI_P3:
                        ffStrbufSetStatic(&camera->colorspace, "DCI-P3");
                        break;
                    case MFVideoPrimaries_ACES:
                        ffStrbufSetStatic(&camera->colorspace, "ACES");
                        break;
                    case (MFVideoPrimaries) 13: // MFVideoPrimaries_Display_P3
                        ffStrbufSetStatic(&camera->colorspace, "Display P3");
                        break;
                    default:
                        break;
                }
            }

            MFGetAttributeSize(type, MF_MT_FRAME_SIZE, &camera->width, &camera->height);
            break;
        }
    }

    if (devices) {
        CoTaskMemFree(devices);
    }

    return nullptr;
}