14ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine/*
24ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine * Copyright (C) 2011 The Android Open Source Project
34ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine *
44ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine * Licensed under the Apache License, Version 2.0 (the "License");
54ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine * you may not use this file except in compliance with the License.
64ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine * You may obtain a copy of the License at
74ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine *
84ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine *      http://www.apache.org/licenses/LICENSE-2.0
94ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine *
104ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine * Unless required by applicable law or agreed to in writing, software
114ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine * distributed under the License is distributed on an "AS IS" BASIS,
124ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
134ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine * See the License for the specific language governing permissions and
144ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine * limitations under the License.
154ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine */
164ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine
174ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine/*
184ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine * Contains code capturing video frames from a camera device on Windows.
194ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine * This code uses capXxx API, available via capCreateCaptureWindow.
204ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine */
21cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine
227f661af7cfca4b7857d30d598923dd2095f78ff0Andrew Hsieh#include <windows.h>
234ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine#include <vfw.h>
244ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine#include "android/camera/camera-capture.h"
254ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine#include "android/camera/camera-format-converters.h"
264ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine
27c68dbbef0118eab4256acfc0d9430f0e557a82a1Vladimir Chtchetkine#define  E(...)    derror(__VA_ARGS__)
28c68dbbef0118eab4256acfc0d9430f0e557a82a1Vladimir Chtchetkine#define  W(...)    dwarning(__VA_ARGS__)
294ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine#define  D(...)    VERBOSE_PRINT(camera,__VA_ARGS__)
304ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine#define  D_ACTIVE  VERBOSE_CHECK(camera)
314ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine
324ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine/* the T(...) macro is used to dump traffic */
334ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine#define  T_ACTIVE   0
344ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine
354ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine#if T_ACTIVE
364ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine#define  T(...)    VERBOSE_PRINT(camera,__VA_ARGS__)
374ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine#else
384ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine#define  T(...)    ((void)0)
394ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine#endif
404ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine
414ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine/* Default name for the capture window. */
424ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkinestatic const char* _default_window_name = "AndroidEmulatorVC";
434ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine
444ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkinetypedef struct WndCameraDevice WndCameraDevice;
454ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine/* Windows-specific camera device descriptor. */
464ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkinestruct WndCameraDevice {
474ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine    /* Common camera device descriptor. */
484ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine    CameraDevice        header;
494ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine    /* Capture window name. (default is AndroidEmulatorVC) */
504ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine    char*               window_name;
514ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine    /* Input channel (video driver index). (default is 0) */
524ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine    int                 input_channel;
534ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine
544ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine    /*
554ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine     * Set when framework gets initialized.
564ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine     */
574ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine
584ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine    /* Video capturing window. Null indicates that device is not connected. */
594ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine    HWND                cap_window;
604ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine    /* DC for frame bitmap manipulation. Null indicates that frames are not
614ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine     * being capturing. */
624ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine    HDC                 dc;
63cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine    /* Bitmap info for the frames obtained from the video capture driver. */
644ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine    BITMAPINFO*         frame_bitmap;
65cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine     /* Bitmap info to use for GetDIBits calls. We can't really use bitmap info
66cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine      * obtained from the video capture driver, because of the two issues. First,
67cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine      * the driver may return an incompatible 'biCompresstion' value. For instance,
68cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine      * sometimes it returns a "fourcc' pixel format value instead of BI_XXX,
69cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine      * which causes GetDIBits to fail. Second, the bitmap that represents a frame
70cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine      * that has been actually obtained from the device is not necessarily matches
71cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine      * bitmap info that capture driver has returned. Sometimes the captured bitmap
72cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine      * is a 32-bit RGB, while bit count reported by the driver is 16. So, to
73cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine      * address these issues we need to have another bitmap info, that can be used
74cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine      * in GetDIBits calls. */
75cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine    BITMAPINFO*         gdi_bitmap;
764ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine    /* Framebuffer large enough to fit the frame. */
774ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine    uint8_t*            framebuffer;
78cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine    /* Framebuffer size. */
79cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine    size_t              framebuffer_size;
80cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine    /* Framebuffer's pixel format. */
81cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine    uint32_t            pixel_format;
82cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine    /* If != 0, frame bitmap is "top-down". If 0, frame bitmap is "bottom-up". */
83cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine    int                 is_top_down;
849d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine    /* Flags whether frame should be captured using clipboard (1), or via frame
859d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine     * callback (0) */
869d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine    int                 use_clipboard;
879d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine    /* Contains last frame captured via frame callback. */
889d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine    void*               last_frame;
899d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine    /* Byte size of the 'last_frame' buffer. */
909d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine    uint32_t            last_frame_size;
914ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine};
924ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine
934ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine/*******************************************************************************
944ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine *                     CameraDevice routines
954ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine ******************************************************************************/
964ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine
974ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine/* Allocates an instance of WndCameraDevice structure.
984ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine * Return:
994ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine *  Allocated instance of WndCameraDevice structure. Note that this routine
1004ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine *  also sets 'opaque' field in the 'header' structure to point back to the
1014ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine *  containing WndCameraDevice instance.
1024ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine */
1034ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkinestatic WndCameraDevice*
1044ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine_camera_device_alloc(void)
1054ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine{
1064ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine    WndCameraDevice* cd = (WndCameraDevice*)malloc(sizeof(WndCameraDevice));
1074ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine    if (cd != NULL) {
1084ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine        memset(cd, 0, sizeof(WndCameraDevice));
1094ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine        cd->header.opaque = cd;
1104ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine    } else {
1114ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine        E("%s: Unable to allocate WndCameraDevice instance", __FUNCTION__);
1124ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine    }
1134ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine    return cd;
1144ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine}
1154ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine
1164ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine/* Uninitializes and frees WndCameraDevice descriptor.
1174ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine * Note that upon return from this routine memory allocated for the descriptor
1184ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine * will be freed.
1194ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine */
1204ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkinestatic void
1214ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine_camera_device_free(WndCameraDevice* cd)
1224ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine{
1234ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine    if (cd != NULL) {
1244ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine        if (cd->cap_window != NULL) {
125cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine            /* Disconnect from the driver. */
1264ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine            capDriverDisconnect(cd->cap_window);
1274ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine
1284ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine            if (cd->dc != NULL) {
1294ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine                W("%s: Frames should not be capturing at this point",
1304ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine                  __FUNCTION__);
1314ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine                ReleaseDC(cd->cap_window, cd->dc);
1324ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine                cd->dc = NULL;
1334ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine            }
1344ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine            /* Destroy the capturing window. */
1354ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine            DestroyWindow(cd->cap_window);
1364ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine            cd->cap_window = NULL;
1374ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine        }
138cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine        if (cd->gdi_bitmap != NULL) {
139cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine            free(cd->gdi_bitmap);
140cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine        }
1414ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine        if (cd->frame_bitmap != NULL) {
1424ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine            free(cd->frame_bitmap);
1434ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine        }
1444ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine        if (cd->window_name != NULL) {
1454ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine            free(cd->window_name);
1464ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine        }
1474ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine        if (cd->framebuffer != NULL) {
1484ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine            free(cd->framebuffer);
1494ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine        }
1509d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine        if (cd->last_frame != NULL) {
1519d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine            free(cd->last_frame);
1529d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine        }
1534ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine        AFREE(cd);
1544ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine    } else {
1554ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine        W("%s: No descriptor", __FUNCTION__);
1564ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine    }
1574ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine}
1584ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine
159a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine/* Resets camera device after capturing.
160a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine * Since new capture request may require different frame dimensions we must
161a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine * reset frame info cached in the capture window. The only way to do that would
162a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine * be closing, and reopening it again. */
163a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkinestatic void
164a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine_camera_device_reset(WndCameraDevice* cd)
165a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine{
166a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine    if (cd != NULL && cd->cap_window != NULL) {
167a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine        capDriverDisconnect(cd->cap_window);
168a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine        if (cd->dc != NULL) {
169a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine            ReleaseDC(cd->cap_window, cd->dc);
170a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine            cd->dc = NULL;
171a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine        }
172a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine        if (cd->gdi_bitmap != NULL) {
173a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine            free(cd->gdi_bitmap);
174a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine            cd->gdi_bitmap = NULL;
175a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine        }
176a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine        if (cd->frame_bitmap != NULL) {
177a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine            free(cd->frame_bitmap);
178a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine            cd->frame_bitmap = NULL;
179a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine        }
180a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine        if (cd->framebuffer != NULL) {
181a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine            free(cd->framebuffer);
182a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine            cd->framebuffer = NULL;
183a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine        }
1849d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine        if (cd->last_frame != NULL) {
1859d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine            free(cd->last_frame);
1869d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine            cd->last_frame = NULL;
1879d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine        }
1889d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine        cd->last_frame_size = 0;
189a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine
190a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine        /* Recreate the capturing window. */
191a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine        DestroyWindow(cd->cap_window);
192a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine        cd->cap_window = capCreateCaptureWindow(cd->window_name, WS_CHILD, 0, 0,
193a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine                                                0, 0, HWND_MESSAGE, 1);
1949d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine        if (cd->cap_window != NULL) {
1959d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine            /* Save capture window descriptor as window's user data. */
1969d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine            capSetUserData(cd->cap_window, cd);
1979d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine        }
198a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine    }
199a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine}
200a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine
201a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine/* Gets an absolute value out of a signed integer. */
202a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkinestatic __inline__ int
203a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine_abs(int val)
204a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine{
205a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine    return (val < 0) ? -val : val;
206a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine}
207a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine
2089d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine/* Callback that is invoked when a frame gets captured in capGrabFrameNoStop */
2099d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkinestatic LRESULT CALLBACK
2109d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine_on_captured_frame(HWND hwnd, LPVIDEOHDR hdr)
2119d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine{
2129d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine    /* Capture window descriptor is saved in window's user data. */
2139d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine    WndCameraDevice* wcd = (WndCameraDevice*)capGetUserData(hwnd);
2149d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine
2159d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine    /* Reallocate frame buffer (if needed) */
2169d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine    if (wcd->last_frame_size < hdr->dwBytesUsed) {
2179d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine        wcd->last_frame_size = hdr->dwBytesUsed;
2189d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine        if (wcd->last_frame != NULL) {
2199d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine            free(wcd->last_frame);
2209d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine        }
2219d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine        wcd->last_frame = malloc(wcd->last_frame_size);
2229d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine    }
2239d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine
2249d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine    /* Copy captured frame. */
2259d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine    memcpy(wcd->last_frame, hdr->lpData, hdr->dwBytesUsed);
2269d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine
227a3338e7214cd0c69912866c6d71d8700c6ab35e2Vladimir Chtchetkine    /* If biCompression is set to default (RGB), set correct pixel format
228a3338e7214cd0c69912866c6d71d8700c6ab35e2Vladimir Chtchetkine     * for converters. */
229a3338e7214cd0c69912866c6d71d8700c6ab35e2Vladimir Chtchetkine    if (wcd->frame_bitmap->bmiHeader.biCompression == BI_RGB) {
230a3338e7214cd0c69912866c6d71d8700c6ab35e2Vladimir Chtchetkine        if (wcd->frame_bitmap->bmiHeader.biBitCount == 32) {
231a3338e7214cd0c69912866c6d71d8700c6ab35e2Vladimir Chtchetkine            wcd->pixel_format = V4L2_PIX_FMT_BGR32;
232a3338e7214cd0c69912866c6d71d8700c6ab35e2Vladimir Chtchetkine        } else if (wcd->frame_bitmap->bmiHeader.biBitCount == 16) {
233a3338e7214cd0c69912866c6d71d8700c6ab35e2Vladimir Chtchetkine            wcd->pixel_format = V4L2_PIX_FMT_RGB565;
234a3338e7214cd0c69912866c6d71d8700c6ab35e2Vladimir Chtchetkine        } else {
235a3338e7214cd0c69912866c6d71d8700c6ab35e2Vladimir Chtchetkine            wcd->pixel_format = V4L2_PIX_FMT_BGR24;
236a3338e7214cd0c69912866c6d71d8700c6ab35e2Vladimir Chtchetkine        }
237a3338e7214cd0c69912866c6d71d8700c6ab35e2Vladimir Chtchetkine    } else {
238a3338e7214cd0c69912866c6d71d8700c6ab35e2Vladimir Chtchetkine        wcd->pixel_format = wcd->frame_bitmap->bmiHeader.biCompression;
239a3338e7214cd0c69912866c6d71d8700c6ab35e2Vladimir Chtchetkine    }
240a3338e7214cd0c69912866c6d71d8700c6ab35e2Vladimir Chtchetkine
2419d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine    return (LRESULT)0;
2429d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine}
2439d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine
2444ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine/*******************************************************************************
2454ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine *                     CameraDevice API
2464ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine ******************************************************************************/
2474ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine
2484ed09fd35085c96ae8edbda87757187f75eeac8dVladimir ChtchetkineCameraDevice*
249cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkinecamera_device_open(const char* name, int inp_channel)
2504ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine{
2514ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine    WndCameraDevice* wcd;
2524ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine
2534ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine    /* Allocate descriptor and initialize windows-specific fields. */
2544ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine    wcd = _camera_device_alloc();
2554ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine    if (wcd == NULL) {
2564ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine        E("%s: Unable to allocate WndCameraDevice instance", __FUNCTION__);
2574ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine        return NULL;
2584ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine    }
2594ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine    wcd->window_name = (name != NULL) ? ASTRDUP(name) :
2604ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine                                        ASTRDUP(_default_window_name);
2614ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine    if (wcd->window_name == NULL) {
2624ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine        E("%s: Unable to save window name", __FUNCTION__);
2634ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine        _camera_device_free(wcd);
2644ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine        return NULL;
2654ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine    }
2664ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine    wcd->input_channel = inp_channel;
2674ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine
2684ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine    /* Create capture window that is a child of HWND_MESSAGE window.
2694ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine     * We make it invisible, so it doesn't mess with the UI. Also
2704ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine     * note that we supply standard HWND_MESSAGE window handle as
2714ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine     * the parent window, since we don't want video capturing
2724ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine     * machinery to be dependent on the details of our UI. */
2734ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine    wcd->cap_window = capCreateCaptureWindow(wcd->window_name, WS_CHILD, 0, 0,
2744ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine                                             0, 0, HWND_MESSAGE, 1);
2754ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine    if (wcd->cap_window == NULL) {
276cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine        E("%s: Unable to create video capturing window '%s': %d",
277cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine          __FUNCTION__, wcd->window_name, GetLastError());
2784ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine        _camera_device_free(wcd);
2794ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine        return NULL;
2804ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine    }
2819d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine    /* Save capture window descriptor as window's user data. */
2829d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine    capSetUserData(wcd->cap_window, wcd);
2834ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine
284cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine    return &wcd->header;
285cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine}
286cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine
287cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkineint
288cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkinecamera_device_start_capturing(CameraDevice* cd,
289cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine                              uint32_t pixel_format,
290cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine                              int frame_width,
291cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine                              int frame_height)
292cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine{
293cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine    WndCameraDevice* wcd;
294cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine    HBITMAP bm_handle;
295cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine    BITMAP  bitmap;
296cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine    size_t format_info_size;
2979d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine    CAPTUREPARMS cap_param;
298cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine
299cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine    if (cd == NULL || cd->opaque == NULL) {
300cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine        E("%s: Invalid camera device descriptor", __FUNCTION__);
301cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine        return -1;
302cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine    }
303cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine    wcd = (WndCameraDevice*)cd->opaque;
304cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine
305cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine    /* wcd->dc is an indicator of capturing: !NULL - capturing, NULL - not */
306cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine    if (wcd->dc != NULL) {
307cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine        W("%s: Capturing is already on on device '%s'",
308cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine          __FUNCTION__, wcd->window_name);
309cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine        return 0;
310cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine    }
311cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine
3124ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine    /* Connect capture window to the video capture driver. */
3134ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine    if (!capDriverConnect(wcd->cap_window, wcd->input_channel)) {
314cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine        return -1;
3154ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine    }
3164ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine
317a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine    /* Get current frame information from the driver. */
3184ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine    format_info_size = capGetVideoFormatSize(wcd->cap_window);
3194ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine    if (format_info_size == 0) {
3204ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine        E("%s: Unable to get video format size: %d",
3214ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine          __FUNCTION__, GetLastError());
322a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine        _camera_device_reset(wcd);
323cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine        return -1;
3244ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine    }
3254ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine    wcd->frame_bitmap = (BITMAPINFO*)malloc(format_info_size);
3264ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine    if (wcd->frame_bitmap == NULL) {
3274ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine        E("%s: Unable to allocate frame bitmap info buffer", __FUNCTION__);
328a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine        _camera_device_reset(wcd);
329cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine        return -1;
3304ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine    }
3314ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine    if (!capGetVideoFormat(wcd->cap_window, wcd->frame_bitmap,
332cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine                           format_info_size)) {
3334ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine        E("%s: Unable to obtain video format: %d", __FUNCTION__, GetLastError());
334a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine        _camera_device_reset(wcd);
335cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine        return -1;
3364ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine    }
3374ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine
338a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine    /* Lets see if we need to set different frame dimensions */
339a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine    if (wcd->frame_bitmap->bmiHeader.biWidth != frame_width ||
340a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine            abs(wcd->frame_bitmap->bmiHeader.biHeight) != frame_height) {
341a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine        /* Dimensions don't match. Set new frame info. */
342a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine        wcd->frame_bitmap->bmiHeader.biWidth = frame_width;
343a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine        wcd->frame_bitmap->bmiHeader.biHeight = frame_height;
344a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine        /* We need to recalculate image size, since the capture window / driver
345a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine         * will use image size provided by us. */
346a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine        if (wcd->frame_bitmap->bmiHeader.biBitCount == 24) {
347a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine            /* Special case that may require WORD boundary alignment. */
348a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine            uint32_t bpl = (frame_width * 3 + 1) & ~1;
349a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine            wcd->frame_bitmap->bmiHeader.biSizeImage = bpl * frame_height;
350a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine        } else {
351a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine            wcd->frame_bitmap->bmiHeader.biSizeImage =
352a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine                (frame_width * frame_height * wcd->frame_bitmap->bmiHeader.biBitCount) / 8;
353a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine        }
354a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine        if (!capSetVideoFormat(wcd->cap_window, wcd->frame_bitmap,
355a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine                               format_info_size)) {
356a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine            E("%s: Unable to set video format: %d", __FUNCTION__, GetLastError());
357a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine            _camera_device_reset(wcd);
358a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine            return -1;
359a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine        }
360a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine    }
361a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine
362cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine    if (wcd->frame_bitmap->bmiHeader.biCompression > BI_PNG) {
363cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine        D("%s: Video capturing driver has reported pixel format %.4s",
364cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine          __FUNCTION__, (const char*)&wcd->frame_bitmap->bmiHeader.biCompression);
3654ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine    }
3664ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine
367cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine    /* Most of the time frame bitmaps come in "bottom-up" form, where its origin
368cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine     * is the lower-left corner. However, it could be in the normal "top-down"
369cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine     * form with the origin in the upper-left corner. So, we must adjust the
370cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine     * biHeight field, since the way "top-down" form is reported here is by
371cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine     * setting biHeight to a negative value. */
372cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine    if (wcd->frame_bitmap->bmiHeader.biHeight < 0) {
373cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine        wcd->frame_bitmap->bmiHeader.biHeight =
374cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine            -wcd->frame_bitmap->bmiHeader.biHeight;
375cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine        wcd->is_top_down = 1;
376cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine    } else {
377cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine        wcd->is_top_down = 0;
3784ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine    }
3794ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine
3804ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine    /* Get DC for the capturing window that will be used when we deal with
3814ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine     * bitmaps obtained from the camera device during frame capturing. */
3824ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine    wcd->dc = GetDC(wcd->cap_window);
3834ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine    if (wcd->dc == NULL) {
3844ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine        E("%s: Unable to obtain DC for %s: %d",
3854ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine          __FUNCTION__, wcd->window_name, GetLastError());
386a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine        _camera_device_reset(wcd);
3874ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine        return -1;
3884ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine    }
3894ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine
3909d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine    /* Setup some capture parameters. */
3919d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine    if (capCaptureGetSetup(wcd->cap_window, &cap_param, sizeof(cap_param))) {
3929d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine        /* Use separate thread to capture video stream. */
3939d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine        cap_param.fYield = TRUE;
3949d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine        /* Don't show any dialogs. */
3959d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine        cap_param.fMakeUserHitOKToCapture = FALSE;
3969d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine        capCaptureSetSetup(wcd->cap_window, &cap_param, sizeof(cap_param));
3979d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine    }
3989d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine
399cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine    /*
400cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine     * At this point we need to grab a frame to properly setup framebuffer, and
401cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine     * calculate pixel format. The problem is that bitmap information obtained
402cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine     * from the driver doesn't necessarily match the actual bitmap we're going to
403cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine     * obtain via capGrabFrame / capEditCopy / GetClipboardData
404cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine     */
405cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine
406cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine    /* Grab a frame, and post it to the clipboard. Not very effective, but this
407cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine     * is how capXxx API is operating. */
408cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine    if (!capGrabFrameNoStop(wcd->cap_window) ||
409cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine        !capEditCopy(wcd->cap_window) ||
410cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine        !OpenClipboard(wcd->cap_window)) {
411cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine        E("%s: Device '%s' is unable to save frame to the clipboard: %d",
412cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine          __FUNCTION__, wcd->window_name, GetLastError());
413a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine        _camera_device_reset(wcd);
414cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine        return -1;
415cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine    }
416cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine
417cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine    /* Get bitmap handle saved into clipboard. Note that bitmap is still
418cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine     * owned by the clipboard here! */
419cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine    bm_handle = (HBITMAP)GetClipboardData(CF_BITMAP);
420cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine    if (bm_handle == NULL) {
421cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine        E("%s: Device '%s' is unable to obtain frame from the clipboard: %d",
422cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine          __FUNCTION__, wcd->window_name, GetLastError());
423cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine        CloseClipboard();
424a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine        _camera_device_reset(wcd);
425cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine        return -1;
426cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine    }
427cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine
428cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine    /* Get bitmap object that is initialized with the actual bitmap info. */
429cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine    if (!GetObject(bm_handle, sizeof(BITMAP), &bitmap)) {
430cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine        E("%s: Device '%s' is unable to obtain frame's bitmap: %d",
431cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine          __FUNCTION__, wcd->window_name, GetLastError());
4329d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine        EmptyClipboard();
433cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine        CloseClipboard();
434a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine        _camera_device_reset(wcd);
435cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine        return -1;
436cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine    }
437cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine
438cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine    /* Now that we have all we need in 'bitmap' */
4399d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine    EmptyClipboard();
440cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine    CloseClipboard();
441cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine
442cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine    /* Make sure that dimensions match. Othewise - fail. */
443cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine    if (wcd->frame_bitmap->bmiHeader.biWidth != bitmap.bmWidth ||
444cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine        wcd->frame_bitmap->bmiHeader.biHeight != bitmap.bmHeight ) {
445cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine        E("%s: Requested dimensions %dx%d do not match the actual %dx%d",
446cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine          __FUNCTION__, frame_width, frame_height,
447cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine          wcd->frame_bitmap->bmiHeader.biWidth,
448cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine          wcd->frame_bitmap->bmiHeader.biHeight);
449a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine        _camera_device_reset(wcd);
450cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine        return -1;
451cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine    }
452cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine
453cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine    /* Create bitmap info that will be used with GetDIBits. */
454cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine    wcd->gdi_bitmap = (BITMAPINFO*)malloc(wcd->frame_bitmap->bmiHeader.biSize);
455cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine    if (wcd->gdi_bitmap == NULL) {
456cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine        E("%s: Unable to allocate gdi bitmap info", __FUNCTION__);
457a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine        _camera_device_reset(wcd);
458cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine        return -1;
459cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine    }
460cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine    memcpy(wcd->gdi_bitmap, wcd->frame_bitmap,
461cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine           wcd->frame_bitmap->bmiHeader.biSize);
462cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine    wcd->gdi_bitmap->bmiHeader.biCompression = BI_RGB;
463cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine    wcd->gdi_bitmap->bmiHeader.biBitCount = bitmap.bmBitsPixel;
464cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine    wcd->gdi_bitmap->bmiHeader.biSizeImage = bitmap.bmWidthBytes * bitmap.bmWidth;
465cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine    /* Adjust GDI's bitmap biHeight for proper frame direction ("top-down", or
466cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine     * "bottom-up") We do this trick in order to simplify pixel format conversion
467cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine     * routines, where we always assume "top-down" frames. The trick he is to
468cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine     * have negative biHeight in 'gdi_bitmap' if driver provides "bottom-up"
469cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine     * frames, and positive biHeight in 'gdi_bitmap' if driver provides "top-down"
470cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine     * frames. This way GetGDIBits will always return "top-down" frames. */
471cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine    if (wcd->is_top_down) {
472cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine        wcd->gdi_bitmap->bmiHeader.biHeight =
473cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine            wcd->frame_bitmap->bmiHeader.biHeight;
474cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine    } else {
475cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine        wcd->gdi_bitmap->bmiHeader.biHeight =
476cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine            -wcd->frame_bitmap->bmiHeader.biHeight;
477cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine    }
478cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine
479cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine    /* Allocate framebuffer. */
480cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine    wcd->framebuffer = (uint8_t*)malloc(wcd->gdi_bitmap->bmiHeader.biSizeImage);
481cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine    if (wcd->framebuffer == NULL) {
482cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine        E("%s: Unable to allocate %d bytes for framebuffer",
483cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine          __FUNCTION__, wcd->gdi_bitmap->bmiHeader.biSizeImage);
484a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine        _camera_device_reset(wcd);
485cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine        return -1;
486cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine    }
487cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine
488cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine    /* Lets see what pixel format we will use. */
489cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine    if (wcd->gdi_bitmap->bmiHeader.biBitCount == 16) {
490cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine        wcd->pixel_format = V4L2_PIX_FMT_RGB565;
491cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine    } else if (wcd->gdi_bitmap->bmiHeader.biBitCount == 24) {
4926ef999d93532fdb0b9f22e14bf2095e4d3ee0355Vladimir Chtchetkine        wcd->pixel_format = V4L2_PIX_FMT_BGR24;
493cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine    } else if (wcd->gdi_bitmap->bmiHeader.biBitCount == 32) {
4946ef999d93532fdb0b9f22e14bf2095e4d3ee0355Vladimir Chtchetkine        wcd->pixel_format = V4L2_PIX_FMT_BGR32;
495cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine    } else {
496cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine        E("%s: Unsupported number of bits per pixel %d",
497cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine          __FUNCTION__, wcd->gdi_bitmap->bmiHeader.biBitCount);
498a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine        _camera_device_reset(wcd);
499cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine        return -1;
500cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine    }
501cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine
502a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine    D("%s: Capturing device '%s': %d bits per pixel in %.4s [%dx%d] frame",
503cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine      __FUNCTION__, wcd->window_name, wcd->gdi_bitmap->bmiHeader.biBitCount,
504cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine      (const char*)&wcd->pixel_format, wcd->frame_bitmap->bmiHeader.biWidth,
505cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine      wcd->frame_bitmap->bmiHeader.biHeight);
506cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine
5079d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine    /* Try to setup capture frame callback. */
5089d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine    wcd->use_clipboard = 1;
5099d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine    if (capSetCallbackOnFrame(wcd->cap_window, _on_captured_frame)) {
5109d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine        /* Callback is set. Don't use clipboard when capturing frames. */
5119d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine        wcd->use_clipboard = 0;
5129d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine    }
5139d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine
5144ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine    return 0;
5154ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine}
5164ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine
5174ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkineint
5184ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkinecamera_device_stop_capturing(CameraDevice* cd)
5194ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine{
5204ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine    WndCameraDevice* wcd;
5214ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine    if (cd == NULL || cd->opaque == NULL) {
5224ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine        E("%s: Invalid camera device descriptor", __FUNCTION__);
5234ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine        return -1;
5244ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine    }
5254ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine    wcd = (WndCameraDevice*)cd->opaque;
526cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine
5279d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine    /* Disable frame callback. */
5289d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine    capSetCallbackOnFrame(wcd->cap_window, NULL);
5299d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine
530cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine    /* wcd->dc is the indicator of capture. */
5314ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine    if (wcd->dc == NULL) {
532cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine        W("%s: Device '%s' is not capturing video",
533cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine          __FUNCTION__, wcd->window_name);
5344ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine        return 0;
5354ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine    }
5364ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine    ReleaseDC(wcd->cap_window, wcd->dc);
5374ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine    wcd->dc = NULL;
5384ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine
539a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine    /* Reset the device in preparation for the next capture. */
540a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine    _camera_device_reset(wcd);
541cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine
5424ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine    return 0;
5434ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine}
5444ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine
5459d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine/* Capture frame using frame callback.
5469d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine * Parameters and return value for this routine matches _camera_device_read_frame
5479d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine */
5489d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkinestatic int
5499d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine_camera_device_read_frame_callback(WndCameraDevice* wcd,
5509d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine                                   ClientFrameBuffer* framebuffers,
5519d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine                                   int fbs_num,
5529d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine                                   float r_scale,
5539d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine                                   float g_scale,
5549d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine                                   float b_scale,
5559d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine                                   float exp_comp)
5564ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine{
5579d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine    /* Grab the frame. Note that this call will cause frame callback to be
5589d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine     * invoked before capGrabFrameNoStop returns. */
5599d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine    if (!capGrabFrameNoStop(wcd->cap_window) || wcd->last_frame == NULL) {
5609d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine        E("%s: Device '%s' is unable to grab a frame: %d",
5619d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine          __FUNCTION__, wcd->window_name, GetLastError());
5624ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine        return -1;
5634ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine    }
5644ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine
5659d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine    /* Convert framebuffer. */
5669d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine    return convert_frame(wcd->last_frame,
567a3338e7214cd0c69912866c6d71d8700c6ab35e2Vladimir Chtchetkine                         wcd->pixel_format,
5689d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine                         wcd->frame_bitmap->bmiHeader.biSizeImage,
5699d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine                         wcd->frame_bitmap->bmiHeader.biWidth,
5709d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine                         wcd->frame_bitmap->bmiHeader.biHeight,
5719d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine                         framebuffers, fbs_num,
5729d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine                         r_scale, g_scale, b_scale, exp_comp);
5739d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine}
5749d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine
5759d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine/* Capture frame using clipboard.
5769d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine * Parameters and return value for this routine matches _camera_device_read_frame
5779d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine */
5789d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkinestatic int
5799d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine_camera_device_read_frame_clipboard(WndCameraDevice* wcd,
5809d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine                                    ClientFrameBuffer* framebuffers,
5819d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine                                    int fbs_num,
5829d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine                                    float r_scale,
5839d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine                                    float g_scale,
5849d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine                                    float b_scale,
5859d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine                                    float exp_comp)
5869d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine{
5879d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine    HBITMAP bm_handle;
5889d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine
5894ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine    /* Grab a frame, and post it to the clipboard. Not very effective, but this
5904ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine     * is how capXxx API is operating. */
5914ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine    if (!capGrabFrameNoStop(wcd->cap_window) ||
5924ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine        !capEditCopy(wcd->cap_window) ||
5934ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine        !OpenClipboard(wcd->cap_window)) {
594cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine        E("%s: Device '%s' is unable to save frame to the clipboard: %d",
5954ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine          __FUNCTION__, wcd->window_name, GetLastError());
5964ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine        return -1;
5974ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine    }
5984ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine
5994ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine    /* Get bitmap handle saved into clipboard. Note that bitmap is still
6004ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine     * owned by the clipboard here! */
6014ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine    bm_handle = (HBITMAP)GetClipboardData(CF_BITMAP);
6024ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine    if (bm_handle == NULL) {
603cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine        E("%s: Device '%s' is unable to obtain frame from the clipboard: %d",
6044ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine          __FUNCTION__, wcd->window_name, GetLastError());
6059d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine        EmptyClipboard();
606cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine        CloseClipboard();
6074ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine        return -1;
6084ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine    }
6094ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine
610cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine    /* Get bitmap buffer. */
611cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine    if (wcd->gdi_bitmap->bmiHeader.biHeight > 0) {
612cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine        wcd->gdi_bitmap->bmiHeader.biHeight = -wcd->gdi_bitmap->bmiHeader.biHeight;
6134ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine    }
6144ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine
6154ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine    if (!GetDIBits(wcd->dc, bm_handle, 0, wcd->frame_bitmap->bmiHeader.biHeight,
616cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine                   wcd->framebuffer, wcd->gdi_bitmap, DIB_RGB_COLORS)) {
617cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine        E("%s: Device '%s' is unable to transfer frame to the framebuffer: %d",
6184ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine          __FUNCTION__, wcd->window_name, GetLastError());
6199d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine        EmptyClipboard();
620cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine        CloseClipboard();
6214ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine        return -1;
6224ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine    }
6234ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine
624cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine    if (wcd->gdi_bitmap->bmiHeader.biHeight < 0) {
625cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine        wcd->gdi_bitmap->bmiHeader.biHeight = -wcd->gdi_bitmap->bmiHeader.biHeight;
6264ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine    }
6274ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine
6289d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine    EmptyClipboard();
629cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine    CloseClipboard();
630cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine
631cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine    /* Convert framebuffer. */
632cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine    return convert_frame(wcd->framebuffer,
633cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine                         wcd->pixel_format,
634cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine                         wcd->gdi_bitmap->bmiHeader.biSizeImage,
635cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine                         wcd->frame_bitmap->bmiHeader.biWidth,
636cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine                         wcd->frame_bitmap->bmiHeader.biHeight,
63737fb84f8b26e3061c1ccb404bf4c962eed5e6057Vladimir Chtchetkine                         framebuffers, fbs_num,
63837fb84f8b26e3061c1ccb404bf4c962eed5e6057Vladimir Chtchetkine                         r_scale, g_scale, b_scale, exp_comp);
6394ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine}
6404ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine
6419d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkineint
6429d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkinecamera_device_read_frame(CameraDevice* cd,
6439d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine                         ClientFrameBuffer* framebuffers,
6449d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine                         int fbs_num,
6459d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine                         float r_scale,
6469d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine                         float g_scale,
6479d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine                         float b_scale,
6489d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine                         float exp_comp)
6499d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine{
6509d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine    WndCameraDevice* wcd;
6519d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine
6529d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine    /* Sanity checks. */
6539d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine    if (cd == NULL || cd->opaque == NULL) {
6549d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine        E("%s: Invalid camera device descriptor", __FUNCTION__);
6559d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine        return -1;
6569d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine    }
6579d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine    wcd = (WndCameraDevice*)cd->opaque;
6589d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine    if (wcd->dc == NULL) {
6599d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine        W("%s: Device '%s' is not captuing video",
6609d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine          __FUNCTION__, wcd->window_name);
6619d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine        return -1;
6629d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine    }
6639d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine
6649d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine    /* Dispatch the call to an appropriate routine: grabbing a frame using
6659d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine     * clipboard, or using a frame callback. */
6669d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine    return wcd->use_clipboard ?
6679d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine        _camera_device_read_frame_clipboard(wcd, framebuffers, fbs_num, r_scale,
6689d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine                                            g_scale, b_scale, exp_comp) :
6699d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine        _camera_device_read_frame_callback(wcd, framebuffers, fbs_num, r_scale,
6709d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine                                           g_scale, b_scale, exp_comp);
6719d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine}
6729d5d34571cb8f596e6ed215e5778c94b8207a33bVladimir Chtchetkine
6734ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkinevoid
6744ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkinecamera_device_close(CameraDevice* cd)
6754ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine{
6764ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine    /* Sanity checks. */
6774ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine    if (cd == NULL || cd->opaque == NULL) {
6784ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine        E("%s: Invalid camera device descriptor", __FUNCTION__);
6794ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine    } else {
6804ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine        WndCameraDevice* wcd = (WndCameraDevice*)cd->opaque;
6814ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine        _camera_device_free(wcd);
6824ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine    }
6834ed09fd35085c96ae8edbda87757187f75eeac8dVladimir Chtchetkine}
684cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine
685cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkineint
686cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkineenumerate_camera_devices(CameraInfo* cis, int max)
687cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine{
6888d9a7a5fa4bd32f9b8aeb7b9e3bd047a475539abVladimir Chtchetkine/* Array containing emulated webcam frame dimensions.
6898d9a7a5fa4bd32f9b8aeb7b9e3bd047a475539abVladimir Chtchetkine * capXxx API provides device independent frame dimensions, by scaling frames
6908d9a7a5fa4bd32f9b8aeb7b9e3bd047a475539abVladimir Chtchetkine * received from the device to whatever dimensions were requested by the user.
6918d9a7a5fa4bd32f9b8aeb7b9e3bd047a475539abVladimir Chtchetkine * So, we can just use a small set of frame dimensions to emulate.
6928d9a7a5fa4bd32f9b8aeb7b9e3bd047a475539abVladimir Chtchetkine */
6938d9a7a5fa4bd32f9b8aeb7b9e3bd047a475539abVladimir Chtchetkinestatic const CameraFrameDim _emulate_dims[] =
6948d9a7a5fa4bd32f9b8aeb7b9e3bd047a475539abVladimir Chtchetkine{
6958d9a7a5fa4bd32f9b8aeb7b9e3bd047a475539abVladimir Chtchetkine  /* Emulates 640x480 frame. */
6968d9a7a5fa4bd32f9b8aeb7b9e3bd047a475539abVladimir Chtchetkine  {640, 480},
6978d9a7a5fa4bd32f9b8aeb7b9e3bd047a475539abVladimir Chtchetkine  /* Emulates 352x288 frame (required by camera framework). */
6988d9a7a5fa4bd32f9b8aeb7b9e3bd047a475539abVladimir Chtchetkine  {352, 288},
699e080a45d3e29e41bce06669b79d5eb04f0fd05efVladimir Chtchetkine  /* Emulates 320x240 frame (required by camera framework). */
700e080a45d3e29e41bce06669b79d5eb04f0fd05efVladimir Chtchetkine  {320, 240},
7018d9a7a5fa4bd32f9b8aeb7b9e3bd047a475539abVladimir Chtchetkine  /* Emulates 176x144 frame (required by camera framework). */
7028d9a7a5fa4bd32f9b8aeb7b9e3bd047a475539abVladimir Chtchetkine  {176, 144}
7038d9a7a5fa4bd32f9b8aeb7b9e3bd047a475539abVladimir Chtchetkine};
704cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine    int inp_channel, found = 0;
705cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine
706cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine    for (inp_channel = 0; inp_channel < 10 && found < max; inp_channel++) {
707cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine        char name[256];
708cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine        CameraDevice* cd;
709cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine
710cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine        snprintf(name, sizeof(name), "%s%d", _default_window_name, found);
711cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine        cd = camera_device_open(name, inp_channel);
712cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine        if (cd != NULL) {
713cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine            WndCameraDevice* wcd = (WndCameraDevice*)cd->opaque;
714cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine
715cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine            /* Unfortunately, on Windows we have to start capturing in order to get the
7168d9a7a5fa4bd32f9b8aeb7b9e3bd047a475539abVladimir Chtchetkine             * actual frame properties. */
717cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine            if (!camera_device_start_capturing(cd, V4L2_PIX_FMT_RGB32, 640, 480)) {
7188d9a7a5fa4bd32f9b8aeb7b9e3bd047a475539abVladimir Chtchetkine                cis[found].frame_sizes = (CameraFrameDim*)malloc(sizeof(_emulate_dims));
719cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine                if (cis[found].frame_sizes != NULL) {
720b8dcaffaf7dcb0c795d2776abf3bb75196f8527cVladimir Chtchetkine                    char disp_name[24];
721b8dcaffaf7dcb0c795d2776abf3bb75196f8527cVladimir Chtchetkine                    sprintf(disp_name, "webcam%d", found);
722b8dcaffaf7dcb0c795d2776abf3bb75196f8527cVladimir Chtchetkine                    cis[found].display_name = ASTRDUP(disp_name);
723cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine                    cis[found].device_name = ASTRDUP(name);
724b8dcaffaf7dcb0c795d2776abf3bb75196f8527cVladimir Chtchetkine                    cis[found].direction = ASTRDUP("front");
725cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine                    cis[found].inp_channel = inp_channel;
7268d9a7a5fa4bd32f9b8aeb7b9e3bd047a475539abVladimir Chtchetkine                    cis[found].frame_sizes_num = sizeof(_emulate_dims) / sizeof(*_emulate_dims);
7278d9a7a5fa4bd32f9b8aeb7b9e3bd047a475539abVladimir Chtchetkine                    memcpy(cis[found].frame_sizes, _emulate_dims, sizeof(_emulate_dims));
728cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine                    cis[found].pixel_format = wcd->pixel_format;
729cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine                    cis[found].in_use = 0;
730cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine                    found++;
731cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine                } else {
732cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine                    E("%s: Unable to allocate dimensions", __FUNCTION__);
733cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine                }
734a83cf909c84fccbf99863446825014b871f74cecVladimir Chtchetkine                camera_device_stop_capturing(cd);
735cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine            } else {
736cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine                /* No more cameras. */
737cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine                camera_device_close(cd);
738cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine                break;
739cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine            }
740cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine            camera_device_close(cd);
741cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine        } else {
742cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine            /* No more cameras. */
743cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine            break;
744cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine        }
745cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine    }
746cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine
747cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine    return found;
748cf1c2c70dd99e7d78816ba9a558f9ed8c016862bVladimir Chtchetkine}
749