UVC host library

Dependents:   LifeCam WebcamServer

uvc/uvc.cpp

Committer:
va009039
Date:
2012-07-31
Revision:
0:b0f04c137829

File content as of revision 0:b0f04c137829:

#include "mbed.h"
#include "uvc.h"

//#define __DEBUG
//#define __DEBUG3
#include "mydbg.h"
#include "usb_itd.h"

uvc::uvc(int cam)
{
    DBG("cam=%d\n", cam);
    DBG_ASSERT(cam >= 0);
    m_cam = cam;
    m_init = false;
    m_connect = false;
    m_int_seq = 0;
    m_iso_seq = 0;
    m_width = 0;
    m_height = 0;
    m_payload = PAYLOAD_MJPEG;
    m_FormatIndex = 0;
    m_FrameIndex = 0;
    m_FrameInterval = 0;
    m_PacketSize = 0;
    m_stream = NULL;
    for(int i = 0; i <= 15; i++) {
        ReportConditionCode[i] = 0;
    }
    clearOnResult();
}

uvc::~uvc()
{
    clearOnResult();
}

int uvc::setup()
{
    if (!m_init) {
        return _init();
    }
    return 0;
}

int uvc::get_jpeg(const char* path)
{
    const int size = 4800;
    const int timeout = 5000;
    uint8_t* buf = new uint8_t[size];
    DBG_ASSERT(buf);
    if (buf == NULL) {
        return -1;
    }
    usb_mjpeg mjpeg(buf, size);
    attach(&mjpeg);
    Timer t;
    t.reset();
    t.start();
    while(t.read_ms() < timeout) {
        int stat = isochronous();
        if (mjpeg.status() >= 0) {
            break;
        }
    }
    detach();
    int len = mjpeg.status();
    if (len >= 0) {
        if (path != NULL) {
            FILE *fp = fopen(path, "wb");
            if (fp != NULL) {
                for(int i = 0; i < len; i++) {
                    fputc(buf[i], fp);
                }
                fclose(fp);
            }
        }    
    }
    delete[] buf;
    return len;
}

int uvc::get_jpeg(uint8_t* buf, int size)
{
    //DBG("buf=%p size=%d\n", buf, size);
    const int timeout = 5000;
    usb_mjpeg mjpeg(buf, size);
    attach(&mjpeg);
    Timer t;
    t.reset();
    t.start();
    while(t.read_ms() < timeout) {
        int stat = isochronous();
        if (mjpeg.status() >= 0) {
            break;
        }
    }
    detach();
    int len = mjpeg.status();
    return len;
}

bool uvc::interrupt()
{
    if (!m_init) {
        _init();
    }
    if (m_int_seq == 0) {
        int rc = m_pEpIntIn->transfer(m_int_buf, sizeof(m_int_buf));
        if (rc != USBERR_PROCESSING) {
            return false;
        }
        m_int_seq++;
    }
    int len = m_pEpIntIn->status();
    if (len > 0) {
        m_int_seq = 0;
        DBG_BYTES("interrupt", m_int_buf, len);
        return true;
    }
    return false;
}

#define CC_NOERROR      0x0
#define CC_DATAOVERRUN  0x8
#define CC_DATAUNDERRUN 0x9

inline void DI()
{
    NVIC_DisableIRQ(USB_IRQn);
}

inline void EI()
{
    NVIC_EnableIRQ(USB_IRQn);
} 

int uvc::isochronous()
{
    if (m_iso_seq == 0) {
        uint16_t frame = LPC_USB->HcFmNumber;
        m_iso_frame = frame + 10; // 10msec
        DBG_ASSERT(m_pEpIsoIn->m_itdActive == 0);
        m_iso_seq++;
    }
    if (m_iso_seq == 1) {
        DBG_ASSERT(m_itdCount > 0 && m_itdCount <= 8);
        while(m_pEpIsoIn->m_itdActive < m_itdCount) {
            int len = m_PacketSize * m_FrameCount;
            uint8_t* buf = (uint8_t*)usb_get_bp(len);
            if (buf == NULL) {
                DBG("len=%d\n", len);
                DBG("m_itdCount=%d\n", m_itdCount);
            }
            DBG_ASSERT(buf);
            int rc = m_pEpIsoIn->transfer(m_iso_frame, m_FrameCount, buf, len);
            m_iso_frame += m_FrameCount;
            DBG_ASSERT(rc == USBERR_PROCESSING);
        }
        m_iso_seq++;
    }
    if (m_iso_seq == 2) {
        //DBG("frame:%04X\n", LPC_USB->HcFmNumber);
        while(1) {
            DI();
            bool empty = m_pEpIsoIn->queue_done_itd.empty();
            EI();
            
            if (empty) {
                break;
            }
            
            DI();
            HCITD* itd = m_pEpIsoIn->queue_done_itd.front();
            m_pEpIsoIn->queue_done_itd.pop();
            EI();
            
            m_pEpIsoIn->m_itdActive--;
            usb_itd iso_td(itd);
            //DBG("frame:%04X\n", LPC_USB->HcFmNumber);
            //DBG("itd->Control=%08X\n", itd->Control); 
            int cc = iso_td.ConditionCode();
            DBG_ASSERT(cc >= 0 && cc <= 15);
            ReportConditionCode[cc]++;
            if (cc != CC_NOERROR) {
                DBG3("%04X ERR:%X\n", LPC_USB->HcFmNumber, cc);
                iso_td.free();
                m_iso_seq = 3;
                return -1;
            }
            uint16_t frame = iso_td.StartingFrame();
            int fc = iso_td.FrameCount();
            for(int i = 0; i < fc; i++) {
                int len = iso_td.Length(i);
                if (len > 0) {
                    if (m_stream) {
                        m_stream->input(frame+i, iso_td.BufferPage(i, m_PacketSize), len);
                    }
                    onResult(frame+i, iso_td.BufferPage(i, m_PacketSize), len);
                }
            }
            iso_td.free();
        }
        //DBG("frame:%04X\n", LPC_USB->HcFmNumber);
        m_iso_seq = 1;
        return m_pEpIsoIn->m_itdActive;
    }
    if (m_iso_seq == 3) { // cleanup
        DBG("m_pEpIsoIn->queue_done_itd.size()  :%d\n", m_pEpIsoIn->queue_done_itd.size());
        while(1) {
            DI();
            bool empty = m_pEpIsoIn->queue_done_itd.empty();
            EI();
            
            if (empty) {
                break;
            }
                       
            DI();
            HCITD* itd = m_pEpIsoIn->queue_done_itd.front();
            m_pEpIsoIn->queue_done_itd.pop();
            EI();
            
            m_pEpIsoIn->m_itdActive--;
            usb_itd iso_td(itd);
            iso_td.free();
        }
        if (m_pEpIsoIn->m_itdActive == 0) {
            m_iso_seq = 0;
        }
    }
    return m_pEpIsoIn->m_itdActive;
}

void uvc::attach(usb_stream* stream)
{
    m_stream = stream;
}

void uvc::detach()
{
    m_stream = NULL;
}

void uvc::onResult(uint16_t frame, uint8_t* buf, int len)
{
  if(m_pCbItem && m_pCbMeth)
    (m_pCbItem->*m_pCbMeth)(frame, buf, len);
  else if(m_pCb)
    m_pCb(frame, buf, len);
}

void uvc::setOnResult( void (*pMethod)(uint16_t, uint8_t*, int) )
{
  m_pCb = pMethod;
  m_pCbItem = NULL;
  m_pCbMeth = NULL;
}

void uvc::clearOnResult()
{
  m_pCb = NULL;
  m_pCbItem = NULL;
  m_pCbMeth = NULL;
}