Blob Blame History Raw
/*
 * 1394-Based Digital Camera Control Library
 *
 * Camera control code for Windows
 *
 * Written by
 *   Satofumi Kamimura <satofumi@users.sourceforge.jp>
 *
 * We use CMU 1394 Digital Camera Driver project's source code for this project.
 *   http://www.cs.cmu.edu/~iwan/1394/
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include <windows.h>
#include "dc1394/internal.h"
#include "platform_windows.h"


static dc1394error_t
free_resources(platform_camera_t * cam)
{
    DWORD ret = t1394IsochTearDownStream(cam->device->device_path);
    if (ret) {
        dc1394_log_warning("t1394IsochTearDownStream: Error %ld\n", ret);
        return DC1394_FAILURE;
    }
    return DC1394_SUCCESS;
}


dc1394error_t
dc1394_windows_iso_allocate_channel (platform_camera_t * cam,
                                     uint64_t channels_allowed, int * channel)
{
    return DC1394_SUCCESS;
}

dc1394error_t
dc1394_windows_iso_release_channel (platform_camera_t * cam, int channel)
{
    // \todo implement
    return DC1394_FAILURE;
}

static dc1394error_t
windows_capture_setup (platform_camera_t * craw, uint32_t num_dma_buffers, uint32_t flags)
{
    int i;

    free_resources(craw);

    ULARGE_INTEGER dma_buffer_size;
    const char * device_path = craw->device->device_path;
    t1394_GetHostDmaCapabilities (device_path, NULL, &dma_buffer_size);

    const dc1394video_frame_t * frame = &craw->capture.frames[0];
    int max_buffer_size = frame->total_bytes; //number of bytes needed
    int max_bytes = frame->packet_size / 2;

    PACQUISITION_BUFFER acquisition_buffer =
        dc1394BuildAcquisitonBuffer(max_buffer_size,
                                    (unsigned long)dma_buffer_size.QuadPart,
                                    max_bytes, 0);

    ISOCH_STREAM_PARAMS stream_params;
    if (acquisition_buffer) {
        stream_params.nMaxBufferSize = acquisition_buffer->subBuffers[0].ulSize;
        stream_params.nNumberOfBuffers = (num_dma_buffers * acquisition_buffer->nSubBuffers) + 1;

        // free the buffer: this wouldn't be necessary if this were merged into StartImageAcquisitionEx
        dc1394FreeAcquisitionBuffer(acquisition_buffer);
        acquisition_buffer = NULL;
    } else {
        dc1394_log_error("windows_capture_setup: failed to determine required buffer size and count!");
        return DC1394_FAILURE;
    }

    dc1394speed_t speed;
    if (dc1394_video_get_iso_speed(craw->camera, &speed) != DC1394_SUCCESS) {
        dc1394_log_error("windows_capture_setup: failed to get iso speed!");
        return DC1394_FAILURE;
    }

    uint32_t channel = -1;
    stream_params.fulSpeed = 1 << speed;
    stream_params.nChannel = channel;
    stream_params.nMaxBytesPerFrame = max_bytes;

    DWORD ret = t1394IsochSetupStream(craw->device->device_path,
                                      &stream_params);
    craw->allocated_channel = stream_params.nChannel;
    if (ret != ERROR_SUCCESS) {
        dc1394_log_error("windows_capture_setup: Error on IsochSetupStream: %d\n", ret);
        return DC1394_FAILURE;
    }

#if 1
    // allocate channel/bandwidth if requested
    if (flags & DC1394_CAPTURE_FLAGS_CHANNEL_ALLOC) {
        if (dc1394_iso_allocate_channel (craw->camera, 0, &craw->allocated_channel)
            != DC1394_SUCCESS) {
            return DC1394_FAILURE;
        }
        if (dc1394_video_set_iso_channel (craw->camera, craw->allocated_channel)
            != DC1394_SUCCESS) {
            return DC1394_FAILURE;
        }
    }
#endif
#if 1
    if (flags & DC1394_CAPTURE_FLAGS_BANDWIDTH_ALLOC) {
        unsigned int bandwidth_usage;
        if (dc1394_video_get_bandwidth_usage (craw->camera, &bandwidth_usage)
            != DC1394_SUCCESS) {
            return DC1394_FAILURE;
        }
        if (dc1394_iso_allocate_bandwidth (craw->camera, bandwidth_usage)
            != DC1394_SUCCESS) {
            return DC1394_FAILURE;
        }
        craw->allocated_bandwidth = bandwidth_usage;
    }
#endif

    /* QUEUE the buffers */
    for (i = 0; i < num_dma_buffers; ++i) {
        PACQUISITION_BUFFER buffer =
            dc1394BuildAcquisitonBuffer(max_buffer_size,
                                        dma_buffer_size.QuadPart, max_bytes, i);
        if(!buffer) {
            dc1394_log_error("windows_capture_setup: Error Allocating AcqBuffer %d\n", i);
            return DC1394_FAILURE;
        }

        // add it to our list of buffers
        if (i == 0) {
            craw->pLastBuffer = craw->pFirstBuffer = buffer;
            craw->pLastBuffer->pNextBuffer = craw->pCurrentBuffer = NULL;
        } else {
            craw->pFirstBuffer->pNextBuffer = buffer;
            craw->pFirstBuffer = buffer;
        }
    }

    // all done making buffers
    // open our long term device handle
    HANDLE device;
    if((device = OpenDevice(device_path, TRUE)) == INVALID_HANDLE_VALUE) {
        dc1394_log_error("windows_capture_setup error opening device (%s)\n", craw->device->device_path);
        return DC1394_FAILURE;
    }

    // all done making buffers
    // open our long term device handle
    PACQUISITION_BUFFER buffer;
    for (buffer = craw->pLastBuffer;
         buffer != NULL; buffer = buffer->pNextBuffer) {
        DWORD ret = dc1394AttachAcquisitionBuffer(device, buffer);
        if (ret != ERROR_SUCCESS) {
            dc1394_log_error("windows_capture_setup: Failed to attach buffer %u/%u", buffer->index, num_dma_buffers);
            return DC1394_FAILURE;
        }
    }

    // new: sleep a little while and verify that the buffers were
    // successfully attached this basically catches "Parameter is Incorrect"
    // here instead of confusing users at AcquireImageEx()
    // 50 ms is all it should take for completion routines to fire and
    // propagate in the kernel
    Sleep(50);

    for (buffer = craw->pLastBuffer;
         buffer != NULL; buffer = buffer->pNextBuffer) {
        DWORD dwBytesRet = 0;
        for (unsigned int bb = 0; bb < buffer->nSubBuffers; ++bb) {
            if (!GetOverlappedResult(device,
                                     &(buffer->subBuffers[bb].overLapped),
                                     &dwBytesRet, FALSE)) {
                if (GetLastError() != ERROR_IO_INCOMPLETE) {
                    dc1394_log_error("Buffer validation failed for buffer %u\n", buffer->index,bb);
                    return DC1394_FAILURE;
                }
                // else: this is the actual success case
            } else {
                dc1394_log_error("Buffer %u is unexpectedly ready during pre-listen validation\n", buffer->index, bb);
                return DC1394_FAILURE;
            }
        }
    }

    ret = t1394IsochListen(craw->device->device_path);
    if (ret != ERROR_SUCCESS) {
        dc1394_log_error("Error %08lx on IOCTL_ISOCH_LISTEN\n", ret);
        return DC1394_FAILURE;
    }

    // starting from here we use the ISO channel so we set the flag
    // in the camera struct:
    craw->capture_is_set = 1;

    for (i = 1; i < num_dma_buffers; ++i) {
        memcpy(&craw->capture.frames[i], frame, sizeof (dc1394video_frame_t));
    }
    craw->capture.frames_last_index = num_dma_buffers - 1;

    craw->device_acquisition = device;
    return DC1394_SUCCESS;
}


dc1394error_t
dc1394_windows_capture_setup(platform_camera_t * craw, uint32_t num_dma_buffers,
                             uint32_t flags)
{
    dc1394camera_t * camera = craw->camera;
    dc1394error_t err;

    if (flags & DC1394_CAPTURE_FLAGS_DEFAULT) {
        flags = DC1394_CAPTURE_FLAGS_CHANNEL_ALLOC |
            DC1394_CAPTURE_FLAGS_BANDWIDTH_ALLOC;
    }

    // if capture is already set, abort
    if (craw->capture_is_set > 0) {
        return DC1394_CAPTURE_IS_RUNNING;
    }

    craw->capture.flags = flags;
    craw->allocated_channel = -1;

    // if auto iso is requested, stop ISO (if necessary)
    if (flags & DC1394_CAPTURE_FLAGS_AUTO_ISO) {
        dc1394switch_t is_iso_on;
        dc1394_video_get_transmission(camera, &is_iso_on);
        if (is_iso_on == DC1394_ON) {
            err=dc1394_video_set_transmission(camera, DC1394_OFF);
            DC1394_ERR_RTN(err,"Could not stop ISO!");
        }
    }

    craw->capture.frames =
        malloc (num_dma_buffers * sizeof (dc1394video_frame_t));
    if (!craw->capture.frames) {
        goto fail;
    }

    err = capture_basic_setup(camera, craw->capture.frames);
    if (err != DC1394_SUCCESS) {
        goto fail;
    }

    err = windows_capture_setup (craw, num_dma_buffers, flags);
    if (err != DC1394_SUCCESS) {
        goto fail;
    }

    // if auto iso is requested, start ISO
    if (flags & DC1394_CAPTURE_FLAGS_AUTO_ISO) {
        err=dc1394_video_set_transmission(camera, DC1394_ON);
        DC1394_ERR_RTN(err,"Could not start ISO!");
        craw->iso_auto_started = 1;
    }

    craw->capture.num_dma_buffers = num_dma_buffers;
    return DC1394_SUCCESS;

 fail:
    // free resources if they were allocated
    if (craw->allocated_channel >= 0) {
        if (dc1394_iso_release_channel (camera, craw->allocated_channel)
            != DC1394_SUCCESS)
            dc1394_log_warning("Warning: Could not free ISO channel");
    }
    if (craw->allocated_bandwidth) {
        if (dc1394_iso_release_bandwidth (camera, craw->allocated_bandwidth)
            != DC1394_SUCCESS)
            dc1394_log_warning("Warning: Could not free bandwidth");
    }
    craw->allocated_channel = -1;
    craw->allocated_bandwidth = 0;

    free (craw->capture.frames);
    craw->capture.frames = NULL;
    dc1394_log_error ("Error: Failed to setup DMA capture");

    return DC1394_FAILURE;
}

dc1394error_t
dc1394_windows_capture_stop(platform_camera_t *craw)
{
    DWORD dwBytesRet = 0;
    DWORD ret;

    if (craw->device_acquisition == INVALID_HANDLE_VALUE) {
        dc1394_log_error("StopImageAcquisition: Called with invalid device handle\n");
        return DC1394_FAILURE;
    }

    // Tear down the stream
    ret = free_resources(craw);
    if (ret) {
        dc1394_log_error("free_resources: Error %ld\n", ret);
    }

    // put pCurrentBuffer on the list for the sake of cleanup
    if (craw->pCurrentBuffer != NULL) {
        craw->pCurrentBuffer->pNextBuffer = craw->pLastBuffer;
        craw->pLastBuffer = craw->pCurrentBuffer;
    }

    while (craw->pLastBuffer) {
        if (craw->pLastBuffer != craw->pCurrentBuffer) {
            // check the IO status, just in case
            for (unsigned int ii = 0; ii < craw->pLastBuffer->nSubBuffers; ++ii) {
                if (!GetOverlappedResult(craw->device_acquisition, &craw->pLastBuffer->subBuffers[ii].overLapped, &dwBytesRet, TRUE)) {
                    dc1394_log_warning("dc1394_windows_capture_stop: Warning Buffer %d.%d has not been detached, error = %d\n", craw->pLastBuffer->index,ii,GetLastError());
                }
            }
        }

        // check the IO status, just in case
        for(unsigned int ii = 0; ii<craw->pLastBuffer->nSubBuffers; ++ii) {
            // close event: NOTE: must pre-populate correctly above
            if(craw->pLastBuffer->subBuffers[ii].overLapped.hEvent != NULL) {
                CloseHandle(craw->pLastBuffer->subBuffers[ii].overLapped.hEvent);
                craw->pLastBuffer->subBuffers[ii].overLapped.hEvent = NULL;
            }
        }

        // free data buffer
        if (craw->pLastBuffer->pDataBuf)
            GlobalFree(craw->pLastBuffer->pDataBuf);

        // advance to next buffer
        PACQUISITION_BUFFER pAcqBuffer = craw->pLastBuffer;
        craw->pLastBuffer = craw->pLastBuffer->pNextBuffer;

        // free buffer struct
        GlobalFree(pAcqBuffer);
    }

    // clean up our junk
    if (craw->device_acquisition != INVALID_HANDLE_VALUE) {
        CloseHandle(craw->device_acquisition);
        craw->device_acquisition = INVALID_HANDLE_VALUE;
    }

    craw->pFirstBuffer = craw->pLastBuffer = craw->pCurrentBuffer = NULL;
    free (craw->capture.frames);

    craw->capture_is_set = 0;

    return DC1394_SUCCESS;
}

dc1394error_t
dc1394_windows_capture_dequeue (platform_camera_t * craw,
                                dc1394capture_policy_t policy,
                                dc1394video_frame_t **frame)
{
    LPOVERLAPPED pOverlapped = &(craw->pLastBuffer->subBuffers[craw->pLastBuffer->nSubBuffers - 1].overLapped);
    DWORD dwBytesRet = 0;

    BOOL ready = 0;
	
    // default: return NULL frame if no new frames/error/etc.
    *frame=NULL;
    
    switch (policy) {
    case DC1394_CAPTURE_POLICY_WAIT:
        ready=GetOverlappedResult(craw->device_acquisition, pOverlapped, &dwBytesRet, TRUE);
        break;
    case DC1394_CAPTURE_POLICY_POLL:
        ready=GetOverlappedResult(craw->device_acquisition, pOverlapped, &dwBytesRet, FALSE);
        break;
    }

    if (ready) {
        craw->pCurrentBuffer = craw->pLastBuffer;

        // advance the buffer queue
        craw->pLastBuffer = craw->pLastBuffer->pNextBuffer;

        // handle the single-buffer case
        // (again, this would be more clear as simply "head" and "tail"
        if (craw->pLastBuffer == NULL) {
            craw->pFirstBuffer = NULL;
        }
    }
    else if (policy==DC1394_CAPTURE_POLICY_POLL)
        return DC1394_SUCCESS;

    if (!craw->pCurrentBuffer) {
        return DC1394_FAILURE;
    }

    // flatten the buffer before returning the internal pointer
    // note: this can only be made more efficient via the "smart" way discussed in CamRGB.cpp: provide
    // an stl-containerlike iterator mechanism that "hides" the flattened-or-not-ness.  Putting that here
    // would, however, break the external API in a way that might require a majorversion bump...
    dc1394FlattenAcquisitionBuffer(craw->pCurrentBuffer);
    unsigned long length = craw->pCurrentBuffer->ulBufferSize;

    dc1394capture_t * capture = &craw->capture;
    *frame = &capture->frames[capture->frames_last_index];
    (*frame)->image = craw->pCurrentBuffer->pFrameStart;
    (*frame)->image_bytes = length;
    capture->frames_last_index =
        (capture->frames_last_index + 1) % capture->num_dma_buffers;
    return DC1394_SUCCESS;
}

dc1394error_t
dc1394_windows_capture_enqueue (platform_camera_t * craw,
                                dc1394video_frame_t * frame)
{
    if (craw->pCurrentBuffer != NULL) {
        if(dc1394AttachAcquisitionBuffer(craw->device_acquisition, craw->pCurrentBuffer) != ERROR_SUCCESS) {
            dc1394_log_error("AcquireImage: Error Reattaching current buffer!");
            return DC1394_FAILURE;
        }

        // push m_pCurrentBuffer onto the Buffer Queue
        // Note: m_pFirstBuffer is the most recently attached buffer, which is at the end of the queue (confusing names...)
        if(craw->pFirstBuffer == NULL) {
            // there is only one buffer, and we just attached it
            craw->pLastBuffer = craw->pCurrentBuffer;
        } else {
            // current buffer goes onto the end of the queue
            craw->pFirstBuffer->pNextBuffer = craw->pCurrentBuffer;
        }

        // current buffer is now the most recently attached buffer
        craw->pFirstBuffer = craw->pCurrentBuffer;

        // which marks the end of the line
        craw->pFirstBuffer->pNextBuffer = NULL;

        // and means that there is, for now, no "current" buffer
        craw->pCurrentBuffer = NULL;
    }

    return DC1394_SUCCESS;
}