video streaming using websocket. but,streaming is very slower than 0.1fps.
Dependencies: BaseUsbHost EthernetInterface WebSocketClient mbed-rtos mbed
Fork of BaseUsbHost_example by
main.cpp@7:5dc595bbff58, 2013-02-19 (annotated)
- Committer:
- va009039
- Date:
- Tue Feb 19 15:50:06 2013 +0000
- Revision:
- 7:5dc595bbff58
- Parent:
- 6:420a86583681
video streaming using websocket
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
va009039 | 6:420a86583681 | 1 | // VideoStreaming/main.cpp 2013/2/20 |
va009039 | 6:420a86583681 | 2 | #include "EthernetInterface.h" |
va009039 | 6:420a86583681 | 3 | #include "Websocket.h" |
va009039 | 3:6ae9a03a6145 | 4 | #include "BaseUsbHost.h" |
va009039 | 3:6ae9a03a6145 | 5 | #include "UvcCam.h" |
va009039 | 3:6ae9a03a6145 | 6 | #include "decodeMJPEG.h" |
va009039 | 3:6ae9a03a6145 | 7 | #include "MyThread.h" |
va009039 | 3:6ae9a03a6145 | 8 | |
va009039 | 6:420a86583681 | 9 | #define CHANNEL "public-ch" |
va009039 | 6:420a86583681 | 10 | #define URL "ws://sockets.mbed.org/ws/"CHANNEL"/rw" |
va009039 | 6:420a86583681 | 11 | #define VIEWER "http://va009039-mbed.appspot.com/VideoStreaming/"CHANNEL"/viewer" |
va009039 | 6:420a86583681 | 12 | #define FRAME_LIMIT 4 |
va009039 | 6:420a86583681 | 13 | #define IMAGE_BUFFER_SIZE (1024*3) |
va009039 | 3:6ae9a03a6145 | 14 | |
va009039 | 6:420a86583681 | 15 | Serial term(USBTX, USBRX); |
va009039 | 3:6ae9a03a6145 | 16 | DigitalOut led1(LED1), led2(LED2), led3(LED3), led4(LED4); |
va009039 | 3:6ae9a03a6145 | 17 | |
va009039 | 5:495f7536897b | 18 | struct ImageBuffer { |
va009039 | 6:420a86583681 | 19 | uint16_t pos; |
va009039 | 6:420a86583681 | 20 | uint8_t buf[IMAGE_BUFFER_SIZE]; |
va009039 | 5:495f7536897b | 21 | void clear() { pos = 0; } |
va009039 | 5:495f7536897b | 22 | int size() { return pos; } |
va009039 | 5:495f7536897b | 23 | void put(uint8_t c) { |
va009039 | 5:495f7536897b | 24 | if (pos < sizeof(buf)) { |
va009039 | 5:495f7536897b | 25 | buf[pos++] = c; |
va009039 | 5:495f7536897b | 26 | } |
va009039 | 5:495f7536897b | 27 | } |
va009039 | 5:495f7536897b | 28 | }; |
va009039 | 5:495f7536897b | 29 | |
va009039 | 5:495f7536897b | 30 | Mail<ImageBuffer, 1> mail_box; |
va009039 | 5:495f7536897b | 31 | |
va009039 | 6:420a86583681 | 32 | class Capture : public MyThread, public decodeMJPEG { |
va009039 | 3:6ae9a03a6145 | 33 | public: |
va009039 | 6:420a86583681 | 34 | Capture(BaseUvc* cam) : m_cam(cam) { |
va009039 | 6:420a86583681 | 35 | m_cam->setOnResult(this, &Capture::callback_motion_jpeg); |
va009039 | 5:495f7536897b | 36 | m_buf = NULL; |
va009039 | 3:6ae9a03a6145 | 37 | } |
va009039 | 6:420a86583681 | 38 | private: |
va009039 | 6:420a86583681 | 39 | ImageBuffer* m_buf; |
va009039 | 6:420a86583681 | 40 | BaseUvc* m_cam; |
va009039 | 6:420a86583681 | 41 | |
va009039 | 6:420a86583681 | 42 | // from decodeMJPEG |
va009039 | 3:6ae9a03a6145 | 43 | virtual void outputJPEG(uint8_t c, int status) { |
va009039 | 5:495f7536897b | 44 | if (m_buf == NULL && status == JPEG_START) { |
va009039 | 5:495f7536897b | 45 | m_buf = mail_box.alloc(); |
va009039 | 5:495f7536897b | 46 | if (m_buf) { |
va009039 | 5:495f7536897b | 47 | m_buf->clear(); |
va009039 | 5:495f7536897b | 48 | } |
va009039 | 3:6ae9a03a6145 | 49 | } |
va009039 | 5:495f7536897b | 50 | if (m_buf) { |
va009039 | 5:495f7536897b | 51 | m_buf->put(c); |
va009039 | 5:495f7536897b | 52 | if (status == JPEG_END) { |
va009039 | 5:495f7536897b | 53 | mail_box.put(m_buf); |
va009039 | 5:495f7536897b | 54 | m_buf = NULL; |
va009039 | 3:6ae9a03a6145 | 55 | } |
va009039 | 3:6ae9a03a6145 | 56 | } |
va009039 | 3:6ae9a03a6145 | 57 | } |
va009039 | 3:6ae9a03a6145 | 58 | |
va009039 | 3:6ae9a03a6145 | 59 | void callback_motion_jpeg(uint16_t frame, uint8_t* buf, int len) { |
va009039 | 6:420a86583681 | 60 | inputPacket(buf, len); // to decodeMJPEG |
va009039 | 3:6ae9a03a6145 | 61 | } |
va009039 | 3:6ae9a03a6145 | 62 | |
va009039 | 3:6ae9a03a6145 | 63 | virtual void run() { |
va009039 | 6:420a86583681 | 64 | while(1) { |
va009039 | 6:420a86583681 | 65 | m_cam->poll(); |
va009039 | 3:6ae9a03a6145 | 66 | } |
va009039 | 3:6ae9a03a6145 | 67 | } |
va009039 | 3:6ae9a03a6145 | 68 | }; |
va009039 | 3:6ae9a03a6145 | 69 | |
va009039 | 6:420a86583681 | 70 | // Copyright (c) 2010 Donatien Garnier (donatiengar [at] gmail [dot] com) |
va009039 | 6:420a86583681 | 71 | int base64enc(const char *input, unsigned int length, char *output, int outputlen) { |
va009039 | 6:420a86583681 | 72 | static const char base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; |
va009039 | 6:420a86583681 | 73 | unsigned int c, c1, c2, c3; |
va009039 | 6:420a86583681 | 74 | |
va009039 | 6:420a86583681 | 75 | if (outputlen < (((length-1)/3)+1)<<2) return -1; |
va009039 | 6:420a86583681 | 76 | |
va009039 | 6:420a86583681 | 77 | for(unsigned int i = 0, j = 0; i<length; i+=3,j+=4) { |
va009039 | 6:420a86583681 | 78 | c1 = ((((unsigned char)*((unsigned char *)&input[i])))); |
va009039 | 6:420a86583681 | 79 | c2 = (length>i+1)?((((unsigned char)*((unsigned char *)&input[i+1])))):0; |
va009039 | 6:420a86583681 | 80 | c3 = (length>i+2)?((((unsigned char)*((unsigned char *)&input[i+2])))):0; |
va009039 | 6:420a86583681 | 81 | |
va009039 | 6:420a86583681 | 82 | c = ((c1 & 0xFC) >> 2); |
va009039 | 6:420a86583681 | 83 | output[j+0] = base64[c]; |
va009039 | 6:420a86583681 | 84 | c = ((c1 & 0x03) << 4) | ((c2 & 0xF0) >> 4); |
va009039 | 6:420a86583681 | 85 | output[j+1] = base64[c]; |
va009039 | 6:420a86583681 | 86 | c = ((c2 & 0x0F) << 2) | ((c3 & 0xC0) >> 6); |
va009039 | 6:420a86583681 | 87 | output[j+2] = (length>i+1)?base64[c]:'='; |
va009039 | 6:420a86583681 | 88 | c = (c3 & 0x3F); |
va009039 | 6:420a86583681 | 89 | output[j+3] = (length>i+2)?base64[c]:'='; |
va009039 | 6:420a86583681 | 90 | } |
va009039 | 6:420a86583681 | 91 | output[(((length-1)/3)+1)<<2] = '\0'; |
va009039 | 6:420a86583681 | 92 | return 0; |
va009039 | 6:420a86583681 | 93 | } |
va009039 | 6:420a86583681 | 94 | |
va009039 | 6:420a86583681 | 95 | #define CHUNK (3*20) |
va009039 | 6:420a86583681 | 96 | |
va009039 | 6:420a86583681 | 97 | void buf_to_websocket(Websocket*ws, ImageBuffer* buf) { |
va009039 | 6:420a86583681 | 98 | Timer t; |
va009039 | 6:420a86583681 | 99 | int send_bytes = 0; |
va009039 | 6:420a86583681 | 100 | t.reset(); |
va009039 | 6:420a86583681 | 101 | t.start(); |
va009039 | 6:420a86583681 | 102 | char output[CHUNK/3*4+1]; |
va009039 | 6:420a86583681 | 103 | for(int i = 0; i < buf->size(); i += CHUNK) { |
va009039 | 6:420a86583681 | 104 | int len = buf->size() - i; |
va009039 | 6:420a86583681 | 105 | if (len > CHUNK) { |
va009039 | 6:420a86583681 | 106 | len = CHUNK; |
va009039 | 6:420a86583681 | 107 | } |
va009039 | 6:420a86583681 | 108 | base64enc(reinterpret_cast<const char*>(buf->buf+i), len, output, sizeof(output)); |
va009039 | 6:420a86583681 | 109 | term.printf("%s\n", output); |
va009039 | 6:420a86583681 | 110 | send_bytes += ws->send(output); |
va009039 | 6:420a86583681 | 111 | } |
va009039 | 6:420a86583681 | 112 | strcpy(output, "."); |
va009039 | 6:420a86583681 | 113 | term.printf("%s\n", output); |
va009039 | 6:420a86583681 | 114 | send_bytes += ws->send(output); |
va009039 | 6:420a86583681 | 115 | term.printf("websocket: send %d bytes %d ms\n", send_bytes, t.read_ms()); |
va009039 | 6:420a86583681 | 116 | } |
va009039 | 6:420a86583681 | 117 | |
va009039 | 3:6ae9a03a6145 | 118 | void no_memory () { |
va009039 | 3:6ae9a03a6145 | 119 | error("Failed to allocate memory!\n"); |
va009039 | 3:6ae9a03a6145 | 120 | } |
va009039 | 3:6ae9a03a6145 | 121 | |
va009039 | 3:6ae9a03a6145 | 122 | int main() { |
va009039 | 6:420a86583681 | 123 | term.baud(921600); |
va009039 | 6:420a86583681 | 124 | term.printf("%s\n", __FILE__); |
va009039 | 3:6ae9a03a6145 | 125 | set_new_handler(no_memory); |
va009039 | 3:6ae9a03a6145 | 126 | |
va009039 | 6:420a86583681 | 127 | EthernetInterface eth; |
va009039 | 6:420a86583681 | 128 | eth.init(); //Use DHCP |
va009039 | 6:420a86583681 | 129 | int r = eth.connect(); |
va009039 | 6:420a86583681 | 130 | if (r != 0) { |
va009039 | 6:420a86583681 | 131 | error("mbed is not connected to the Internet. %d\n", r); |
va009039 | 6:420a86583681 | 132 | } |
va009039 | 6:420a86583681 | 133 | term.printf("IP Address is %s\n\r", eth.getIPAddress()); |
va009039 | 6:420a86583681 | 134 | Websocket* ws = new Websocket(URL); |
va009039 | 6:420a86583681 | 135 | if (!ws->connect()) { |
va009039 | 6:420a86583681 | 136 | error("mbed is not connected to websocket "URL); |
va009039 | 6:420a86583681 | 137 | } |
va009039 | 6:420a86583681 | 138 | |
va009039 | 3:6ae9a03a6145 | 139 | BaseUsbHost* usbHost = new BaseUsbHost(); |
va009039 | 3:6ae9a03a6145 | 140 | ControlEp* ctlEp = new ControlEp; // root hub |
va009039 | 6:420a86583681 | 141 | if (UsbHub::check(ctlEp)) { |
va009039 | 6:420a86583681 | 142 | UsbHub* hub = new UsbHub(ctlEp); |
va009039 | 6:420a86583681 | 143 | ctlEp = hub->search<UvcCam>(0); |
va009039 | 6:420a86583681 | 144 | if (ctlEp == NULL) { |
va009039 | 6:420a86583681 | 145 | error("UVC Camera is not connected in USB hub.\n"); |
va009039 | 5:495f7536897b | 146 | } |
va009039 | 6:420a86583681 | 147 | } else if (!UvcCam::check(ctlEp)) { |
va009039 | 3:6ae9a03a6145 | 148 | error("UVC Camera is not connected.\n"); |
va009039 | 3:6ae9a03a6145 | 149 | } |
va009039 | 6:420a86583681 | 150 | UvcCam* cam = new UvcCam(UVC_MJPEG, UVC_160x120, _5FPS, ctlEp); |
va009039 | 6:420a86583681 | 151 | |
va009039 | 6:420a86583681 | 152 | Capture* capture_th = new Capture(cam); |
va009039 | 6:420a86583681 | 153 | capture_th->set_stack(512); |
va009039 | 6:420a86583681 | 154 | capture_th->start(); |
va009039 | 6:420a86583681 | 155 | |
va009039 | 6:420a86583681 | 156 | term.printf("\n\n"VIEWER"\n\n"); |
va009039 | 3:6ae9a03a6145 | 157 | |
va009039 | 6:420a86583681 | 158 | int frame = 0; |
va009039 | 6:420a86583681 | 159 | for(int n = 0;; n++) { |
va009039 | 6:420a86583681 | 160 | osEvent evt = mail_box.get(200); |
va009039 | 5:495f7536897b | 161 | if (evt.status == osEventMail) { |
va009039 | 5:495f7536897b | 162 | ImageBuffer *buf = reinterpret_cast<ImageBuffer*>(evt.value.p); |
va009039 | 6:420a86583681 | 163 | if (frame < 10) { |
va009039 | 6:420a86583681 | 164 | term.printf("Capture stack used: %d/%d bytes\n", capture_th->stack_used(), capture_th->stack_size()); |
va009039 | 6:420a86583681 | 165 | term.printf("image size: %d bytes\n", buf->size()); |
va009039 | 6:420a86583681 | 166 | } |
va009039 | 6:420a86583681 | 167 | if (frame++ < FRAME_LIMIT || FRAME_LIMIT==(-1)) { |
va009039 | 6:420a86583681 | 168 | led2 = 1; |
va009039 | 6:420a86583681 | 169 | buf_to_websocket(ws, buf); |
va009039 | 6:420a86583681 | 170 | led2 = 0; |
va009039 | 5:495f7536897b | 171 | } |
va009039 | 5:495f7536897b | 172 | mail_box.free(buf); |
va009039 | 5:495f7536897b | 173 | led4 = !led4; |
va009039 | 5:495f7536897b | 174 | } |
va009039 | 6:420a86583681 | 175 | led1 = ws->is_connected(); |
va009039 | 3:6ae9a03a6145 | 176 | } |
va009039 | 3:6ae9a03a6145 | 177 | } |