├── .gitignore ├── Makefile ├── README.md ├── bin └── .gitkeep ├── images ├── screenshot1.png └── screenshot2.png ├── include ├── dev.h ├── huffman.h ├── merrno.h ├── print.h ├── server.h └── service.h ├── log └── log ├── obj └── .gitkeep └── src ├── dev.c ├── main.c ├── merrno.c ├── print.c ├── server.c └── service.c /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Object files 5 | *.o 6 | *.ko 7 | *.obj 8 | *.elf 9 | /obj/* 10 | 11 | # Linker output 12 | *.ilk 13 | *.map 14 | *.exp 15 | 16 | # Precompiled Headers 17 | *.gch 18 | *.pch 19 | 20 | # Libraries 21 | *.lib 22 | *.a 23 | *.la 24 | *.lo 25 | 26 | # Shared objects (inc. Windows DLLs) 27 | *.dll 28 | *.so 29 | *.so.* 30 | *.dylib 31 | 32 | # Executables 33 | *.exe 34 | *.out 35 | *.app 36 | *.i*86 37 | *.x86_64 38 | *.hex 39 | /bin/* 40 | 41 | # Debug files 42 | *.dSYM/ 43 | *.su 44 | *.idb 45 | *.pdb 46 | 47 | # Kernel Module Compile Results 48 | *.mod* 49 | *.cmd 50 | .tmp_versions/ 51 | modules.order 52 | Module.symvers 53 | Mkfile.old 54 | dkms.conf 55 | 56 | !.gitkeep 57 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | OBJS=obj/dev.o obj/merrno.o obj/print.o obj/service.o 2 | CFLAGS=-Iinclude -lpthread 3 | 4 | all: bin/server 5 | 6 | bin/server: src/server.c $(OBJS) 7 | cc -o $@ $^ $(CFLAGS) 8 | 9 | obj/service.o: src/service.c include/merrno.h 10 | cc -o $@ -c $< $(CFLAGS) 11 | 12 | obj/dev.o: src/dev.c include/dev.h include/merrno.h 13 | cc -o $@ -c $< $(CFLAGS) 14 | 15 | obj/merrno.o: src/merrno.c include/merrno.h 16 | cc -o $@ -c $< $(CFLAGS) 17 | 18 | obj/print.o: src/print.c include/print.h include/huffman.h 19 | cc -o $@ -c $< $(CFLAGS) 20 | 21 | clean: 22 | $(RM) $(OBJS) 23 | 24 | distclean: clean 25 | $(RM) bin/server 26 | 27 | .PHONY: clean distclean all -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 基于Linux的视频服务器 2 | 3 | ![](https://github.com/EMSD1701-01/WebCamera/blob/master/images/screenshot2.png) 4 | ![](https://github.com/EMSD1701-01/WebCamera/blob/master/images/screenshot1.png) 5 | -------------------------------------------------------------------------------- /bin/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EMSD1701-01/WebCamera/22ba039606ef329f9d6ba8ffd58d7de83dc09d6d/bin/.gitkeep -------------------------------------------------------------------------------- /images/screenshot1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EMSD1701-01/WebCamera/22ba039606ef329f9d6ba8ffd58d7de83dc09d6d/images/screenshot1.png -------------------------------------------------------------------------------- /images/screenshot2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EMSD1701-01/WebCamera/22ba039606ef329f9d6ba8ffd58d7de83dc09d6d/images/screenshot2.png -------------------------------------------------------------------------------- /include/dev.h: -------------------------------------------------------------------------------- 1 | #ifndef __DEV_H__ 2 | #define __DEV_H__ 3 | 4 | #include 5 | 6 | typedef struct 7 | { 8 | void *start; //映射后的物理首地址 9 | size_t length; //存储映射的地址长度 10 | }Videobuf; 11 | 12 | //摄像头文件描述符 13 | extern int camera_fd; 14 | //存储映射数组 15 | extern Videobuf *buffer; 16 | //记录缓存的数量 17 | extern int bufs_num; 18 | //记录捕获了多少张图像 19 | extern int counter; 20 | //记录已经刷新好的缓存的索引号 21 | extern int okindex; 22 | //临时缓冲区 23 | extern char *tmp_buf; 24 | //记录摄像头打开状态 25 | extern int on_off; 26 | 27 | int init_dev(const char *camDev); 28 | 29 | int uninit_dev(); 30 | 31 | int get_dev_info(); 32 | 33 | int cam_on(); 34 | 35 | int cam_off(); 36 | 37 | int get_frame(); 38 | 39 | 40 | #endif //__DEV_H__ -------------------------------------------------------------------------------- /include/huffman.h: -------------------------------------------------------------------------------- 1 | #ifndef __HUFFMAN_H__ 2 | #define __HUFFMAN_H__ 3 | #define DHT_SIZE 432 4 | 5 | static unsigned char dht_data[DHT_SIZE] = { 6 | 0xff, 0xc4, 0x00, 0x1f, 0x00, 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 7 | 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 8 | 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 9 | 0x0b, 0xff, 0xc4, 0x00, 0xb5, 0x10, 0x00, 0x02, 10 | 0x01, 0x03, 0x03, 0x02, 0x04, 0x03, 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 11 | 0x01, 0x7d, 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 12 | 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, 0x22, 0x71, 13 | 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08, 0x23, 0x42, 14 | 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0, 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 15 | 0x0a, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 16 | 0x27, 0x28, 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, 17 | 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 18 | 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 19 | 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 20 | 0x78, 0x79, 0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x92, 21 | 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 22 | 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 23 | 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 24 | 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 25 | 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2, 0xe3, 0xe4, 26 | 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 27 | 0xf7, 0xf8, 0xf9, 0xfa, 0xff, 0xc4, 0x00, 0x1f, 28 | 0x01, 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 29 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 30 | 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0xff, 0xc4, 0x00, 31 | 0xb5, 0x11, 0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 32 | 0x03, 0x04, 0x07, 0x05, 0x04, 0x04, 0x00, 0x01, 0x02, 0x77, 0x00, 0x01, 33 | 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06, 34 | 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 35 | 0x42, 0x91, 0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 36 | 0x52, 0xf0, 0x15, 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34, 0xe1, 0x25, 37 | 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26, 0x27, 0x28, 38 | 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 39 | 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 40 | 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 41 | 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 42 | 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 43 | 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 44 | 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 45 | 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 46 | 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 47 | 0xd9, 0xda, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 48 | 0xe8, 0xe9, 0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa 49 | }; 50 | 51 | #define JPG_HUFFMAN_TABLE_LENGTH 0x1A0 52 | 53 | const unsigned char JPEGHuffmanTable[JPG_HUFFMAN_TABLE_LENGTH] 54 | = { 55 | 0x00, 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 56 | 0x00, 0x00, 0x00, 0x00, 0x00, 57 | 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 58 | 0x0A, 0x0B, 0x01, 0x00, 0x03, 59 | 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 60 | 0x00, 0x00, 0x00, 0x00, 0x01, 61 | 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x10, 62 | 0x00, 0x02, 0x01, 0x03, 0x03, 63 | 0x02, 0x04, 0x03, 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01, 0x7D, 64 | 0x01, 0x02, 0x03, 0x00, 0x04, 65 | 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, 66 | 0x22, 0x71, 0x14, 0x32, 0x81, 67 | 0x91, 0xA1, 0x08, 0x23, 0x42, 0xB1, 0xC1, 0x15, 0x52, 0xD1, 0xF0, 68 | 0x24, 0x33, 0x62, 0x72, 0x82, 69 | 0x09, 0x0A, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x25, 0x26, 0x27, 0x28, 70 | 0x29, 0x2A, 0x34, 0x35, 0x36, 71 | 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 72 | 0x4A, 0x53, 0x54, 0x55, 0x56, 73 | 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 74 | 0x6A, 0x73, 0x74, 0x75, 0x76, 75 | 0x77, 0x78, 0x79, 0x7A, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 76 | 0x8A, 0x92, 0x93, 0x94, 0x95, 77 | 0x96, 0x97, 0x98, 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 78 | 0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 79 | 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 80 | 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 81 | 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xE1, 0xE2, 82 | 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 83 | 0xE8, 0xE9, 0xEA, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 84 | 0xF9, 0xFA, 0x11, 0x00, 0x02, 85 | 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, 0x07, 0x05, 0x04, 0x04, 0x00, 86 | 0x01, 0x02, 0x77, 0x00, 0x01, 87 | 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 88 | 0x07, 0x61, 0x71, 0x13, 0x22, 89 | 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, 0xA1, 0xB1, 0xC1, 0x09, 0x23, 90 | 0x33, 0x52, 0xF0, 0x15, 0x62, 91 | 0x72, 0xD1, 0x0A, 0x16, 0x24, 0x34, 0xE1, 0x25, 0xF1, 0x17, 0x18, 92 | 0x19, 0x1A, 0x26, 0x27, 0x28, 93 | 0x29, 0x2A, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, 0x45, 94 | 0x46, 0x47, 0x48, 0x49, 0x4A, 95 | 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 96 | 0x66, 0x67, 0x68, 0x69, 0x6A, 97 | 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x82, 0x83, 0x84, 98 | 0x85, 0x86, 0x87, 0x88, 0x89, 99 | 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0xA2, 100 | 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 101 | 0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 102 | 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 103 | 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 104 | 0xD8, 0xD9, 0xDA, 0xE2, 0xE3, 105 | 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xF2, 0xF3, 0xF4, 0xF5, 106 | 0xF6, 0xF7, 0xF8, 0xF9, 0xFA 107 | }; 108 | #endif 109 | -------------------------------------------------------------------------------- /include/merrno.h: -------------------------------------------------------------------------------- 1 | #ifndef __MERRNO_H__ 2 | #define __MERRNO_H__ 3 | 4 | void strErr(const char *msg); 5 | 6 | void sysErr(const char *msg); 7 | 8 | void suc_err(int code, const char *msg); 9 | 10 | #endif //__MERRNO_H__ -------------------------------------------------------------------------------- /include/print.h: -------------------------------------------------------------------------------- 1 | #ifndef __PRINT_H__ 2 | #define __PRINT_H__ 3 | 4 | extern int print_picture(int fd, unsigned char *buf, int size); 5 | 6 | #endif 7 | -------------------------------------------------------------------------------- /include/server.h: -------------------------------------------------------------------------------- 1 | #ifndef __SERVER_H__ 2 | #define __SERVER_H__ 3 | 4 | /** 5 | * 初始化服务器 6 | * @param port 绑定的端口 7 | * @return 如果成功返回一个文件描述符,失败返回-1 8 | */ 9 | int initServer(unsigned short port); 10 | 11 | /** 12 | * responseHttp 13 | */ 14 | int responseHttp(int sockFd); 15 | 16 | int getHttpRequest(int sockFd); 17 | 18 | #endif //__SERVER_H__ -------------------------------------------------------------------------------- /include/service.h: -------------------------------------------------------------------------------- 1 | #ifndef __SERVICE_H__ 2 | #define __SERVICE_H__ 3 | 4 | #include 5 | 6 | /** 7 | * 初始化服务器 8 | * @param port 绑定的端口 9 | * @return 如果成功返回一个serverSocket,失败返回-1 10 | */ 11 | int initServer(unsigned short port); 12 | 13 | int getHttpRequest(int sockFd); 14 | 15 | int responseHttp(int sockFd); 16 | 17 | int sendPictureHeader(int sockFd, size_t size); 18 | 19 | #endif //__SERVICE_H__ -------------------------------------------------------------------------------- /log/log: -------------------------------------------------------------------------------- 1 | 服务端初始化完毕... 2 | format 3 | Req_bufs 4 | Query_buf 5 | Mmap 6 | Query_buf 7 | Mmap 8 | Query_buf 9 | Mmap 10 | Query_buf 11 | Mmap 12 | camera on 13 | Q_buf_init 14 | Q_buf_init 15 | Q_buf_init 16 | Q_buf_init 17 | 服务端初始化完毕... 18 | format 19 | Req_bufs 20 | Query_buf 21 | Mmap 22 | Query_buf 23 | Mmap 24 | Query_buf 25 | Mmap 26 | Query_buf 27 | Mmap 28 | camera on 29 | Q_buf_init 30 | Q_buf_init 31 | Q_buf_init 32 | Q_buf_init 33 | 服务端初始化完毕... 34 | format 35 | Req_bufs 36 | Query_buf 37 | Mmap 38 | Query_buf 39 | Mmap 40 | Query_buf 41 | Mmap 42 | Query_buf 43 | Mmap 44 | camera on 45 | Q_buf_init 46 | Q_buf_init 47 | Q_buf_init 48 | Q_buf_init 49 | 客户端数量: 1 50 | 客户端数量: 1 51 | 服务端初始化完毕... 52 | format 53 | Req_bufs 54 | Query_buf 55 | Mmap 56 | Query_buf 57 | Mmap 58 | Query_buf 59 | Mmap 60 | Query_buf 61 | Mmap 62 | camera on 63 | Q_buf_init 64 | Q_buf_init 65 | Q_buf_init 66 | Q_buf_init 67 | 客户端: 192.168.47.1:1614 连接成功! 68 | 客户端数量: 1 69 | 客户端: 192.168.47.1:1615 连接成功! 70 | 客户端数量: 2 71 | 客户端: 192.168.47.1:1616 连接成功! 72 | 客户端数量: 3 73 | 一个客户端断开连接! 74 | 客户端数量: 2 75 | 一个客户端断开连接! 76 | 客户端数量: 1 77 | 一个客户端断开连接! 78 | 客户端数量: 0 79 | 服务端初始化完毕... 80 | format 81 | Req_bufs 82 | Query_buf 83 | Mmap 84 | Query_buf 85 | Mmap 86 | Query_buf 87 | Mmap 88 | Query_buf 89 | Mmap 90 | camera on 91 | Q_buf_init 92 | Q_buf_init 93 | Q_buf_init 94 | Q_buf_init 95 | 客户端: 172.16.2.107:56181 连接成功! 96 | 客户端数量: 1 97 | 客户端: 172.16.2.107:56182 连接成功! 98 | 客户端数量: 2 99 | 客户端: 172.16.2.107:56183 连接成功! 100 | 客户端数量: 3 101 | 客户端: 172.16.2.107:50635 连接成功! 102 | 客户端数量: 4 103 | 一个客户端断开连接! 104 | 客户端数量: 3 105 | 一个客户端断开连接! 106 | 客户端数量: 2 107 | 客户端: 172.16.2.107:50637 连接成功! 108 | 客户端数量: 3 109 | 客户端: 172.16.2.107:50638 连接成功! 110 | 客户端数量: 4 111 | 一个客户端断开连接! 112 | 客户端数量: 3 113 | 一个客户端断开连接! 114 | 客户端数量: 2 115 | 一个客户端断开连接! 116 | 客户端数量: 1 117 | 一个客户端断开连接! 118 | 客户端数量: 0 119 | 客户端: 172.16.2.107:50640 连接成功! 120 | 客户端数量: 1 121 | 客户端: 172.16.2.107:50641 连接成功! 122 | 客户端数量: 2 123 | 客户端: 172.16.2.107:54780 连接成功! 124 | 客户端数量: 3 125 | 一个客户端断开连接! 126 | 客户端数量: 2 127 | 客户端: 192.168.47.1:1623 连接成功! 128 | 客户端数量: 3 129 | 客户端: 192.168.47.1:1624 连接成功! 130 | 客户端数量: 4 131 | 客户端: 172.16.2.107:63108 连接成功! 132 | 客户端数量: 5 133 | 一个客户端断开连接! 134 | 客户端数量: 4 135 | 一个客户端断开连接! 136 | 客户端数量: 3 137 | 客户端: 172.16.2.150:41765 连接成功! 138 | 客户端数量: 4 139 | 客户端: 172.16.2.95:51235 连接成功! 140 | 客户端数量: 5 141 | 客户端: 172.16.2.150:41769 连接成功! 142 | 客户端数量: 6 143 | 客户端: 172.16.2.63:60447 连接成功! 144 | 客户端数量: 7 145 | 一个客户端断开连接! 146 | 客户端数量: 6 147 | 客户端: 172.16.2.63:60448 连接成功! 148 | 客户端数量: 7 149 | 一个客户端断开连接! 150 | 客户端数量: 6 151 | 一个客户端断开连接! 152 | 客户端数量: 5 153 | 一个客户端断开连接! 154 | 客户端数量: 4 155 | 一个客户端断开连接! 156 | 客户端数量: 3 157 | 客户端: 192.168.47.1:1644 连接成功! 158 | 客户端数量: 4 159 | 客户端: 192.168.47.1:1645 连接成功! 160 | 客户端数量: 5 161 | 客户端: 172.16.2.63:60449 连接成功! 162 | 客户端数量: 6 163 | 客户端: 172.16.2.150:41770 连接成功! 164 | 客户端数量: 7 165 | 一个客户端断开连接! 166 | 客户端数量: 6 167 | 一个客户端断开连接! 168 | 客户端数量: 5 169 | 一个客户端断开连接! 170 | 客户端数量: 4 171 | 一个客户端断开连接! 172 | 客户端数量: 3 173 | 服务端初始化完毕... 174 | format 175 | Req_bufs 176 | Query_buf 177 | Mmap 178 | Query_buf 179 | Mmap 180 | Query_buf 181 | Mmap 182 | Query_buf 183 | Mmap 184 | camera on 185 | Q_buf_init 186 | Q_buf_init 187 | Q_buf_init 188 | Q_buf_init 189 | 客户端: 192.168.47.1:54071 连接成功! 190 | 客户端数量: 1 191 | 客户端: 192.168.47.1:54070 连接成功! 192 | 一个客户端断开连接! 193 | 客户端数量: 0 194 | -------------------------------------------------------------------------------- /obj/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/EMSD1701-01/WebCamera/22ba039606ef329f9d6ba8ffd58d7de83dc09d6d/obj/.gitkeep -------------------------------------------------------------------------------- /src/dev.c: -------------------------------------------------------------------------------- 1 | #include "dev.h" 2 | #include "merrno.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | static void init_fmt(); 11 | static void init_mmap(); 12 | 13 | //摄像头文件描述符 14 | int camera_fd; 15 | 16 | //存储映射数组 17 | Videobuf *buffer; 18 | //记录缓存的数量 19 | int bufs_num; 20 | //记录捕获了多少张图像 21 | int counter; 22 | //记录已经刷新好的缓存的索引号 23 | int okindex; 24 | char *tmp_buf; 25 | int on_off; 26 | 27 | int init_dev(const char *camDev) 28 | { 29 | //打开摄像头 30 | camera_fd = open(camDev, O_RDWR|O_NONBLOCK); 31 | //初始化视频格式 32 | init_fmt(); 33 | //初始化内存映射 34 | init_mmap(); 35 | 36 | return 0; 37 | } 38 | 39 | int uninit_dev() 40 | { 41 | int i; 42 | for (i = 0; i < bufs_num; ++i) 43 | { 44 | int res = munmap(buffer[i].start, buffer[i].length); 45 | suc_err(res, "munmap"); 46 | } 47 | free(buffer); 48 | free(tmp_buf); 49 | close(camera_fd); 50 | return 0; 51 | } 52 | 53 | int get_dev_info() 54 | { 55 | //获取当前设备的属性 56 | struct v4l2_capability cap; 57 | int res = ioctl(camera_fd,VIDIOC_QUERYCAP,&cap); 58 | 59 | //获取当前设备的输出格式 60 | struct v4l2_format fmt; 61 | fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 62 | res = ioctl(camera_fd,VIDIOC_G_FMT,&fmt); 63 | 64 | //获取当前设备的帧率 65 | struct v4l2_streamparm parm; 66 | parm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 67 | res = ioctl(camera_fd,VIDIOC_G_PARM,&parm); 68 | 69 | //打印输出设备信息: 70 | printf("----------------dev_info---------------\n"); 71 | printf("driver: %s\n",cap.driver); 72 | printf("card: %s\n",cap.card);//摄像头的设备名 73 | printf("bus: %s\n",cap.bus_info); 74 | printf("width: %d\n",fmt.fmt.pix.width);//当前的图像输出宽度 75 | printf("height: %d\n",fmt.fmt.pix.height);//当前的图像输出高度 76 | printf("FPS: %d\n",parm.parm.capture.timeperframe.denominator); 77 | printf("------------------end------------------\n"); 78 | 79 | return 0; 80 | } 81 | 82 | int cam_on() 83 | { 84 | enum v4l2_buf_type type; 85 | type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 86 | int res = ioctl(camera_fd,VIDIOC_STREAMON,&type); 87 | suc_err(res, "camera on"); 88 | 89 | //进行一次缓存刷新 90 | int i; 91 | struct v4l2_buffer buf; 92 | for(i = 0;i < bufs_num;i++) 93 | { 94 | memset(&buf,0,sizeof(buf)); 95 | buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 96 | buf.memory = V4L2_MEMORY_MMAP; 97 | buf.index = i; 98 | res = ioctl(camera_fd,VIDIOC_QBUF,&buf); 99 | suc_err(res,"Q_buf_init"); 100 | } 101 | 102 | return 0; 103 | } 104 | 105 | int cam_off() 106 | { 107 | enum v4l2_buf_type type; 108 | type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 109 | 110 | int res = ioctl(camera_fd, VIDIOC_STREAMOFF, &type); 111 | suc_err(res, "close stream"); 112 | 113 | on_off = 0; 114 | 115 | return 0; 116 | } 117 | 118 | int get_frame() 119 | { 120 | struct v4l2_buffer buf; 121 | int i = 0,res; 122 | counter++; 123 | memset(&buf,0,sizeof(buf)); 124 | buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 125 | buf.memory = V4L2_MEMORY_MMAP; 126 | 127 | fd_set readfds; 128 | FD_ZERO(&readfds); 129 | FD_SET(camera_fd,&readfds); 130 | struct timeval tv;//设置设备响应时间 131 | tv.tv_sec = 1;//秒 132 | tv.tv_usec = 0;//微秒 133 | while(select(camera_fd + 1,&readfds,NULL,NULL,&tv) <= 0) 134 | { 135 | fprintf(stderr,"camera busy,Dq_buf time out\n"); 136 | FD_ZERO(&readfds); 137 | FD_SET(camera_fd,&readfds); 138 | tv.tv_sec = 1; 139 | tv.tv_usec = 0; 140 | } 141 | res = ioctl(camera_fd,VIDIOC_DQBUF,&buf); 142 | //suc_err(res,"Dq_buf"); 143 | 144 | //buf.index表示已经刷新好的可用的缓存索引号 145 | okindex = buf.index; 146 | //更新缓存已用大小 147 | buffer[okindex].length = buf.bytesused; 148 | //第n次捕获图片:(第n回刷新整个缓存队列-第n个缓存被刷新) 149 | //printf("Image_%03d:(%d-%d)\n",counter,counter / bufs_num,okindex); 150 | 151 | //把图像放入缓存队列中(入列) 152 | res = ioctl(camera_fd,VIDIOC_QBUF,&buf); 153 | //suc_err(res,"Q_buf"); 154 | 155 | return 0; 156 | } 157 | 158 | //初始化视频格式 159 | void init_fmt() 160 | { 161 | struct v4l2_format fmt; 162 | memset(&fmt,0,sizeof(fmt)); 163 | fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;//数据流的类型 164 | fmt.fmt.pix.width = 320;//图像的宽度 165 | fmt.fmt.pix.height = 240;//图像的高度 166 | fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG;//彩色空间 167 | fmt.fmt.pix.field = V4L2_FIELD_INTERLACED; 168 | int res = ioctl(camera_fd,VIDIOC_S_FMT,&fmt); 169 | suc_err(res, "format"); 170 | } 171 | 172 | //初始化内存映射 173 | void init_mmap() 174 | { 175 | int res; 176 | //请求缓存 177 | struct v4l2_requestbuffers req; 178 | memset(&req,0,sizeof(req)); 179 | req.count = 4;//缓存数量 180 | req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 181 | req.memory = V4L2_MEMORY_MMAP; 182 | res = ioctl(camera_fd,VIDIOC_REQBUFS,&req); 183 | suc_err(res,"Req_bufs"); 184 | 185 | buffer = calloc(req.count,sizeof(Videobuf)); 186 | struct v4l2_buffer buf; 187 | for(bufs_num = 0;bufs_num < req.count;bufs_num++) 188 | { 189 | memset(&buf,0,sizeof(buf)); 190 | buf.index = bufs_num;//设置缓存索引号 191 | buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 192 | buf.field = V4L2_FIELD_INTERLACED; 193 | buf.memory = V4L2_MEMORY_MMAP; 194 | //读取缓存信息 195 | res = ioctl(camera_fd,VIDIOC_QUERYBUF,&buf); 196 | suc_err(res,"Query_buf"); 197 | //设置缓存大小 198 | buffer[bufs_num].length = buf.length; 199 | //在堆空间中动态分配二级缓存空间 200 | tmp_buf = (char*)calloc(buffer[okindex].length,sizeof(char)); 201 | //将设备文件的地址映射到用户空间的物理地址 202 | buffer[bufs_num].start = mmap(NULL, 203 | buf.length, 204 | PROT_READ|PROT_WRITE, 205 | MAP_SHARED, 206 | camera_fd, 207 | buf.m.offset); 208 | if(buffer[bufs_num].start == MAP_FAILED) 209 | suc_err(-1,"Mmap"); 210 | else 211 | suc_err(0,"Mmap"); 212 | } 213 | } -------------------------------------------------------------------------------- /src/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include "dev.h" 8 | #include "merrno.h" 9 | #include "server.h" 10 | 11 | int main(int argc, char **argv) 12 | { 13 | if(argc < 2) 14 | { 15 | fprintf(stderr, "-Usage: %s \n", argv[0]); 16 | return -1; 17 | } 18 | 19 | int fd = initServer(10000); 20 | if(fd >= 0){ 21 | getHttpRequest(fd); 22 | responseHttp(fd); 23 | } 24 | 25 | close(fd); 26 | 27 | // camera_fd = open(argv[1], O_RDWR | O_NONBLOCK); 28 | // init_dev(); 29 | // cam_on(); 30 | 31 | // get_dev_info(); 32 | 33 | // get_frame(); 34 | 35 | // cam_off(); 36 | 37 | // close(camera_fd); 38 | 39 | return 0; 40 | } -------------------------------------------------------------------------------- /src/merrno.c: -------------------------------------------------------------------------------- 1 | #include "merrno.h" 2 | #include 3 | #include 4 | #include 5 | 6 | void strErr(const char *msg) 7 | { 8 | fprintf(stderr, "%s\n", msg); 9 | } 10 | 11 | void sysErr(const char *msg) 12 | { 13 | fprintf(stderr, "%s: %s\n", msg, strerror(errno)); 14 | } 15 | 16 | void suc_err(int code, const char *msg) 17 | { 18 | if(code < 0){ 19 | sysErr(msg); 20 | }else{ 21 | strErr(msg); 22 | } 23 | } -------------------------------------------------------------------------------- /src/print.c: -------------------------------------------------------------------------------- 1 | #include "print.h" 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include "huffman.h" 12 | 13 | static int 14 | is_huffman(unsigned char *buf) 15 | { 16 | unsigned char *ptbuf; 17 | int i = 0; 18 | ptbuf = buf; 19 | while (((ptbuf[0] << 8) | ptbuf[1]) != 0xffda) { 20 | if (i++ > 2048) 21 | return 0; 22 | if (((ptbuf[0] << 8) | ptbuf[1]) == 0xffc4) 23 | return 1; 24 | ptbuf++; 25 | } 26 | return 0; 27 | } 28 | 29 | extern int 30 | print_picture(int fd, unsigned char *buf, int size) 31 | { 32 | unsigned char *ptdeb, *ptcur = buf; 33 | int sizein; 34 | 35 | 36 | if (!is_huffman(buf)) { 37 | ptdeb = ptcur = buf; 38 | while (((ptcur[0] << 8) | ptcur[1]) != 0xffc0) 39 | ptcur++; 40 | sizein = ptcur - ptdeb; 41 | if( write(fd, buf, sizein) <= 0) return -1; 42 | if( write(fd, dht_data, DHT_SIZE) <= 0) return -1; 43 | if( write(fd, ptcur, size - sizein) <= 0) return -1; 44 | } else { 45 | if( write(fd, ptcur, size) <= 0) return -1; 46 | } 47 | return 0; 48 | } 49 | -------------------------------------------------------------------------------- /src/server.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "dev.h" 13 | #include "merrno.h" 14 | #include "service.h" 15 | #include "print.h" 16 | 17 | #define MAX_CLIENTS_COUNT 128 18 | 19 | static int clients[MAX_CLIENTS_COUNT]; 20 | static size_t clientCount = 0; 21 | 22 | /** 23 | * 获取有效用户数量 24 | * @return 返回有效用户个数 25 | */ 26 | size_t getValidClient(); 27 | 28 | //初始化线程锁 29 | pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; 30 | 31 | /** 32 | * 接收客户端线程 33 | */ 34 | void *tAcceptClient(void *arg) 35 | { 36 | int serverSocket = initServer(*(short*)arg); 37 | if(serverSocket == -1){ 38 | return (void*)NULL; 39 | } 40 | strErr("服务端初始化完毕..."); 41 | 42 | //接收客户端的连接请求 43 | int clientSocket; 44 | struct sockaddr_in clientAddr; 45 | socklen_t clientAddrLen = sizeof(clientAddr); 46 | while(1){ 47 | if((clientSocket = accept(serverSocket, (struct sockaddr*)&clientAddr, &clientAddrLen)) < 0) 48 | { 49 | sysErr("accept"); 50 | }else{ 51 | fprintf(stderr, "客户端: %s:%u 连接成功!\n", inet_ntoa(clientAddr.sin_addr), ntohs(clientAddr.sin_port)); 52 | getHttpRequest(clientSocket); 53 | responseHttp(clientSocket); 54 | 55 | pthread_mutex_lock(&mutex); //加锁 56 | int i; 57 | for(i = 0; i < MAX_CLIENTS_COUNT; i++){ 58 | if(clients[i] == 0){ 59 | clients[i] = clientSocket; 60 | if(i + 1 > clientCount){ 61 | clientCount = i + 1; 62 | } 63 | break; 64 | } 65 | } 66 | fprintf(stderr, "客户端数量: %lu\n", getValidClient()); 67 | pthread_mutex_unlock(&mutex); //解锁 68 | } 69 | } 70 | 71 | //关闭serverSocket 72 | close(serverSocket); 73 | strErr("服务端关闭..."); 74 | } 75 | 76 | int main(int argc, char **argv) 77 | { 78 | //默认端口号 79 | short int port = 10000; 80 | if(argc < 2){ 81 | fprintf(stderr, "-Usage: %s [port]\n", argv[0]); 82 | return -1; 83 | }else if(argc >= 3){ 84 | port = atoi(argv[2]); 85 | } 86 | 87 | // int logFd = open("log/log", O_WRONLY|O_APPEND|O_CREAT, 0644); 88 | // if(logFd < 0){ 89 | // sysErr("log日志打开失败!"); 90 | // }else{ 91 | // dup2(logFd, 1); 92 | // dup2(logFd, 2); 93 | // } 94 | 95 | //忽略管道信号,当客户端结束时不要退出程序 96 | signal(SIGPIPE, SIG_IGN); 97 | 98 | //开启线程监听客户端 99 | pthread_t acceptClientThread; 100 | pthread_create(&acceptClientThread, NULL, tAcceptClient, &port); 101 | 102 | //初始化摄像头设备 103 | init_dev(argv[1]); 104 | //打开摄像头 105 | cam_on(); 106 | get_dev_info(); 107 | 108 | int i; 109 | while(1){ 110 | get_frame(); 111 | 112 | pthread_mutex_lock(&mutex); 113 | for(i = 0; i < clientCount; i++){ 114 | if(clients[i] != 0){ 115 | if(sendPictureHeader(clients[i], buffer[okindex].length)){ 116 | struct sockaddr_in addr; 117 | socklen_t len = sizeof(addr); 118 | fprintf(stderr, "一个客户端断开连接!\n"); 119 | clients[i] = 0; 120 | if(i == clientCount - 1){ 121 | clientCount--; 122 | } 123 | fprintf(stderr, "客户端数量: %lu\n", getValidClient()); 124 | continue; 125 | } 126 | print_picture(clients[i], buffer[okindex].start, buffer[okindex].length); 127 | } 128 | } 129 | pthread_mutex_unlock(&mutex); 130 | } 131 | 132 | //关闭设备 133 | cam_off(); 134 | uninit_dev(); 135 | 136 | return 0; 137 | } 138 | 139 | /** 140 | * 获取有效用户数量 141 | * @return 返回有效用户个数 142 | */ 143 | size_t getValidClient() 144 | { 145 | int i; 146 | size_t ret = 0; 147 | for(i = 0; i < clientCount; i++){ 148 | if(clients[i] != 0){ 149 | ret++; 150 | } 151 | } 152 | return ret; 153 | } 154 | 155 | -------------------------------------------------------------------------------- /src/service.c: -------------------------------------------------------------------------------- 1 | #include "service.h" 2 | #include "merrno.h" 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | static char *trim(char *cmd); 10 | 11 | int initServer(unsigned short port) 12 | { 13 | //创建serverSocket 14 | int serverSocket = socket(AF_INET, SOCK_STREAM, 0); 15 | 16 | //配置地址 17 | struct sockaddr_in serverAddr; 18 | memset(&serverAddr, 0, sizeof(serverAddr)); 19 | serverAddr.sin_family = AF_INET; 20 | serverAddr.sin_port = htons(port); 21 | serverAddr.sin_addr.s_addr = htonl(INADDR_ANY); 22 | 23 | //配置端口可重用 24 | int opt = 1; 25 | setsockopt(serverSocket, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); 26 | 27 | //绑定端口 28 | if(bind(serverSocket, (struct sockaddr*)&serverAddr, sizeof(serverAddr))) 29 | { 30 | sysErr("bind"); 31 | return; 32 | } 33 | 34 | //监听,配置最大连接数 35 | if(listen(serverSocket, SOMAXCONN)) 36 | { 37 | sysErr("listen"); 38 | return; 39 | } 40 | 41 | return serverSocket; 42 | } 43 | 44 | int getHttpRequest(int sockFd) 45 | { 46 | char buffer[1024], ch; 47 | int index = 0; 48 | while(read(sockFd, &ch, 1) > 0) 49 | { 50 | if(ch == '\n') 51 | { 52 | buffer[index] = '\0'; 53 | if(!strcmp("\r", trim(buffer))) 54 | { 55 | break; 56 | } 57 | index = 0; 58 | }else 59 | { 60 | buffer[index++] = ch; 61 | } 62 | } 63 | return 0; 64 | } 65 | 66 | int responseHttp(int sockFd) 67 | { 68 | char buffer[1024]; 69 | sprintf(buffer, "HTTP/1.0 200 OK\r\n" 70 | "Connection: Keep-Alive\r\n" 71 | "Server: Network camera\r\n" 72 | "Cache-Control: no-cache,no-store,must-revalidate,pre-check=0,max-age=0\r\n" 73 | "Pragma: no-cache\r\n" 74 | "Content-Type: multipart/x-mixed-replace;boundary=KK\r\n\r\n"); 75 | if(write(sockFd, buffer, strlen(buffer)) != strlen(buffer)){ 76 | sysErr("responseHttp"); 77 | return -1; 78 | } 79 | return 0; 80 | } 81 | 82 | int sendPictureHeader(int sockFd, size_t size) 83 | { 84 | char buffer[128]; 85 | sprintf(buffer, "--KK\r\n" 86 | "Content-Type: image/jpeg\r\n" 87 | "Content-Length: %lu\r\n\r\n", size); 88 | if(write(sockFd, buffer, strlen(buffer)) != strlen(buffer)){ 89 | sysErr("responseHttp type"); 90 | return -1; 91 | } 92 | return 0; 93 | } 94 | 95 | /** 96 | * 去除命令行两边的空格 97 | */ 98 | char *trim(char *cmd) 99 | { 100 | while(*cmd == ' ') cmd++; 101 | char *end = cmd + strlen(cmd) - 1; 102 | while(*end == ' ') *end-- = '\0'; 103 | return cmd; 104 | } 105 | --------------------------------------------------------------------------------