看看博客,忽然发现很久不更新文章了,最近都在忙着找工作什么的,现在又是感冒发烧,难过得很!
刚好今天实现了基本的客户端,就简单的说一下关于整个实现过程吧,废话不过多说~~
测试环境:acer 4752G ubuntu 12.04
工具:vi Qt4.7
首先要做一个视频监控,第一步就是要有一个摄像头(这是硬条件,没摄像头做个JB对吧),这里采用的是笔记本的自带摄像头。当然其它的也可以,不过要查寻一下基本的驱动架构和支持的图片格式。有的是jpeg的图片格式的,有的是yuyv的图片格式。确保摄像头是好的之后,接下来就是利用vi写一个服务器了,服务器主要工作就是等待客户端的链接,然后负责调度摄像头采集图片,不断的发往客户端就行了~~
以下是我测试用的代码,略微有点乱,注释什么的和打印信息测试什么的都没删掉,讲究用下吧。不想弄它了。。。以下是完整的代码实现。服务器采用系统编程这一块实现~~
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 |
/*=============================================================== * Copyright (C) 2013 All rights reserved. * * #Filename : common.h * #Express : * #Created : 2013-11-8 10:57 * #Compiler : gcc/g++ * #Author : Art's * #Company : Art's * ================================================================*/ #ifndef __COMMON_H_ #define __COMMON_H_ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <assert.h> #include <fcntl.h> #include <unistd.h> #include <errno.h> #include <sys/time.h> #include <sys/stat.h> #include <sys/types.h> #include <sys/mman.h> #include <sys/ioctl.h> #include <netdb.h> #include <linux/videodev2.h> #include <jpeglib.h> #define CLEAR(x) memset(&x,0,sizeof(x)) typedef enum { MJPEG = V4L2_PIX_FMT_MJPEG, YUYV = V4L2_PIX_FMT_YUYV }fmt_t; #define WIDTH 640 #define HEIGHT 480 typedef struct v4l2_capability Cap; // typedef struct v4l2_format Fmt; // typedef struct v4l2_fmtdesc Fmtd; // typedef struct v4l2_streamparm Fps; // typedef struct v4l2_requestbuffers ReqBuf; // typedef struct v4l2_buffer Vbuf; // /** * @brief 摄像头配置 */ typedef struct { int cam_fd; //摄像头描述符 fmt_t fmt; //图片格式YUYV MJPEG两种 枚举类型,定义在头 int width; //宽度 int height; //高度 int fps; //针率 int n_buf; //向内存申请的缓冲数 }Cam_conf; /* * @brief 用户缓冲 */ typedef struct { char *start; //起始地址 int len; //缓冲区长度 }Buf; ReqBuf reqbuf; //内核空间 Vbuf vbuf; //队列 Buf *bufs; //用户映射空间 Buf tmp_buf; //一帧图片空间 //Buf share_buf; //线程共享变量 Cam_conf conf; //摄像头配置 #endif |
这里是图片的haffman编码之类的东西,我也不怎么懂,网上找的。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 |
#ifndef __HUFFMAN_H__ #define __HUFFMAN_H__ #define DHT_SIZE 432 static unsigned char dht_data[DHT_SIZE] = { 0xff, 0xc4, 0x00, 0x1f, 0x00, 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0xff, 0xc4, 0x00, 0xb5, 0x10, 0x00, 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03, 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01, 0x7d, 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xa1, 0x08, 0x23, 0x42, 0xb1, 0xc1, 0x15, 0x52, 0xd1, 0xf0, 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0a, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xff, 0xc4, 0x00, 0x1f, 0x01, 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0xff, 0xc4, 0x00, 0xb5, 0x11, 0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, 0x07, 0x05, 0x04, 0x04, 0x00, 0x01, 0x02, 0x77, 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, 0xa1, 0xb1, 0xc1, 0x09, 0x23, 0x33, 0x52, 0xf0, 0x15, 0x62, 0x72, 0xd1, 0x0a, 0x16, 0x24, 0x34, 0xe1, 0x25, 0xf1, 0x17, 0x18, 0x19, 0x1a, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xda, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0xfa }; #define JPG_HUFFMAN_TABLE_LENGTH 0x1A0 const unsigned char JPEGHuffmanTable[JPG_HUFFMAN_TABLE_LENGTH] = { 0x00, 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x01, 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x10, 0x00, 0x02, 0x01, 0x03, 0x03, 0x02, 0x04, 0x03, 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01, 0x7D, 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xA1, 0x08, 0x23, 0x42, 0xB1, 0xC1, 0x15, 0x52, 0xD1, 0xF0, 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0A, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0x11, 0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, 0x07, 0x05, 0x04, 0x04, 0x00, 0x01, 0x02, 0x77, 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, 0xA1, 0xB1, 0xC1, 0x09, 0x23, 0x33, 0x52, 0xF0, 0x15, 0x62, 0x72, 0xD1, 0x0A, 0x16, 0x24, 0x34, 0xE1, 0x25, 0xF1, 0x17, 0x18, 0x19, 0x1A, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA }; #endif |
接下来就是具体的关于摄像头那一块的实现,查询出基本的摄像头格式,然后相应的设置摄像头,实现数据的采集
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 |
/** * @file query.h * @brief 摄像头查询模块接口文件 */ #ifndef __QUERY_H_ #define __QUERY_H_ #include "common.h" #include "webcam.h" /** * @brief query_cap * 查询摄像头参数及驱动信息:打印struct v4l2_capability * @param fd 摄像头文件描述符 * @return */ extern void query_cap(int fd); /** * @brief query_fps * 使用VIDIOC_G_PARM查询帧率信息 * @param fd */ extern void query_fps(int fd); /** * @brief query_fmt * 使用VIDIOC_ENUM_FMT查询摄像头信息 * @param fd */ extern void query_fmt(int fd); #endif /** * @file webcam.h * @brief 摄像头配置及操作接口文件 */ #ifndef __WEBCAM_H_ #define __WEBCAM_H_ #include "common.h" /** * @brief cam_open * 打开摄像头 * @param argc * @param argv * @return 文件描述符 */ extern int cam_open(int argc,char *argv[]); /** * @brief cam_close * 关闭摄像头、释放用户缓冲空间 * @param fd */ extern void cam_close(int fd); /** * @brief cam_init * 查询并设置设备属性、格式、帧率,内存映射设置 * @param fd 摄像头 */ void cam_init(Cam_conf *conf); /** * @brief cam_free 释放用户缓冲空间 */ static void cam_free(void); /** * @brief start_capturing * 开启视频流 * @param fd */ void start_capturing(int fd); /** * @brief stop_capturing * 关闭视频流 * @param fd */ void stop_capturing(int fd); /** * @brief set_fmt 设置视频的帧格式 * 根据摄像头像素参数设置Logitech C210:640*480 * @param conf */ static void set_fmt(Cam_conf *conf ); /** * @brief set_fps * 设置帧率,C210 最大帧率30fps * @param fd * @param fps */ static void set_fps(Cam_conf *conf ); /** * @brief init_mmap * 在内核空间申请帧缓冲、映射到用户空间、并入缓冲队列 * @param fd * @param num */ static void init_mmap(Cam_conf *conf ); /** * @brief set_frame_size * 设置图片大小 * @param conf * @return */ static int set_frame_size(Cam_conf *conf); /** * @brief get_frame * 出队一帧数据,拷贝到temp_buf,并入队 * @param conf * @return 成功0,失败-1 */ int get_frame(Cam_conf *conf); /** * @brief process * 保存一帧图片到本地目录 * @param conf */ void process(Cam_conf *conf); /** * @brief is_huffman * 判断图片是否为huffman编码 * @param buf * @return */ static int is_huffman(unsigned char *buf); /** * @brief print_picture * 将图片输出到fd文件中 * @param fd * @param buf * @param size * @return */ int print_picture(int fd, unsigned char *buf, int size); #endif #ifndef __PRINT_H__ #define __PRINT_H__ extern int print_picture(int fd, unsigned char *buf, int size); #endif #ifndef __YUV2JPEG_H__ #define __YUV2JPEG_H__ #include <jpeglib.h> #include "common.h" typedef struct{ struct jpeg_destination_mgr jpg_p; JOCTET * buffer; unsigned char *out_buf; int out_buf_size; unsigned char *out_buf_cur; int *written; }jpg_dst; typedef jpg_dst *jpg_ptr; /* yuyv to jpeg function */ void dst_buffer(j_compress_ptr,unsigned char*,int,int*); int yuv_to_jpeg(unsigned char*, unsigned char*,int, int); #endif 以下是上面四个头文件的函数实现 /** * @file query.h * @brief 摄像头查询模块接口文件 */ #include "query.h" /** * @brief query_cap * 查询摄像头参数及驱动信息:打印struct v4l2_capability * @param fd 摄像头文件描述符 * */ void query_cap(int fd) { Cap cap; CLEAR(cap); if(ioctl(fd,VIDIOC_QUERYCAP,&cap) < 0) //查询摄像头的功能 { perror("query_cap()"); //也是出错处理 exit(EXIT_FAILURE); } printf("===============Camera Info===============\n"); printf("Driver: %s \n",cap.driver); printf("Card: %s \n",cap.card); printf("BusInfo: %s \n",cap.bus_info); printf("Version: %d \n",cap.version); printf("Capabilities: 0x%x \n",cap.capabilities); //printf("Device Cap: 0x%x \n",cap.device_caps); //printf("Reserved: 0x%x \n",cap.reserved); } /** * @brief query_fps * 使用VIDIOC_G_PARM查询帧率信息 * @param fd */ void query_fps(int fd) { Fps fps; CLEAR(fps); fps.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; //设置类型 //ioctl(fd,VIDIOC_G_PARM,&fps); if(ioctl(fd,VIDIOC_G_PARM,&fps) < 0) { fprintf(stderr,"Usage:query_fps:%s error\n",strerror(errno)); exit(EXIT_FAILURE); } printf("=======================Camera Fps==============\n"); printf("Type:%d \n",fps.type); printf("Capture->capability:%d \n",fps.parm.capture.capability); printf("Capture->capturemode:%d \n",fps.parm.capture.capturemode); //1为高清模式 printf("Capture->captureparm->timeperframe->numerator:%d \n",\ fps.parm.capture.timeperframe.numerator); //默认为1 printf("Capture->captureparm->timeperframe->denominator:%d \n",\ fps.parm.capture.timeperframe.denominator); //帧率 printf("Capture->extendedmode:%d \n",fps.parm.capture.extendedmode); printf("Capture->readbuffer:%d \n",fps.parm.capture.readbuffers); //printf("Capture->outputparm->\n",fps.parm.output.); } /** * @brief query_fmt * 使用VIDIOC_ENUM_FMT查询摄像头信息 * @param fd */ void query_fmt(int fd) { Fmtd fmtd; CLEAR(fmtd); fmtd.index = 0; fmtd.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; printf("================Fmtd Info================\n"); while(-1 != ioctl(fd,VIDIOC_ENUM_FMT,&fmtd)) { printf("query fmtd %d %s\n",fmtd.index,fmtd.description); fmtd.index++; } } /** * @file webcam.h * @brief 摄像头配置及操作接口文件 */ #include "webcam.h" //#include "print.h" #include "huffman.h" #include "yuv2jpeg.h" #define HEADERFREAM 0xaf /** * @brief cam_open * 打开摄像头 * @param argc * @param argv * @return 文件描述符 */ int cam_open(int argc,char *argv[]) { if(argc < 2) { fprintf(stderr,"Usage:webcam[/dev/videoX]:%s\error\n",strerror(errno)); exit(EXIT_FAILURE); } int fd = open(argv[1],O_RDWR,0); if(fd < 0) { fprintf(stderr,"can't open %s:%s \n",argv[1],strerror(errno)); } return fd; } /** * @brief cam_close * 关闭摄像头、释放用户缓冲空间 * @param fd */ void cam_close(int fd) { close(fd); fd = -1; cam_free(); } /** * @brief set_fmt 设置视频的帧格式 * 根据摄像头像素参数设置Logitech C210:640*480 * @param conf */ static void set_fmt(Cam_conf *conf) { Fmt cam_fmt; //视屏格式结构提 CLEAR(cam_fmt); //清零 cam_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; //默认设置为次宏定义 cam_fmt.fmt.pix.width = conf->width; //设置宽度 cam_fmt.fmt.pix.height = conf->height; //设置高度 cam_fmt.fmt.pix.pixelformat = conf->fmt; //设置图片格式 cam_fmt.fmt.pix.field = V4L2_FIELD_ANY; //设置采集field域,默认值 if(ioctl(conf->cam_fd,VIDIOC_S_FMT,&cam_fmt) == -1) //写入 { fprintf(stderr,"Usage:set_fmt:%s error\n",strerror(errno)); exit(EXIT_FAILURE); } } /** * @brief set_frame_size * 设置图片大小 * @param conf * @return */ static int set_frame_size(Cam_conf *conf) { if(conf == NULL) //判断是否为空 { return -1; } tmp_buf.len = WIDTH*HEIGHT<<1;//tmp_buf为全局函数 switch(conf->fmt) { case MJPEG: tmp_buf.start = (unsigned char *)calloc(1,tmp_buf.len); //return 0; break; case YUYV: tmp_buf.start = (unsigned char *)calloc(1,tmp_buf.len*3/2); //return 0; break; } return tmp_buf.len; } /** * @brief set_fps * 设置帧率,C210 最大帧率30fps * @param fd * @param fps */ static void set_fps(Cam_conf *conf ) { Fps fps; CLEAR(fps); fps.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; //默认设置为次宏定义 fps.parm.capture.capturemode = 1; //高清模式 fps.parm.capture.timeperframe.numerator = 1; // fps.parm.capture.timeperframe.denominator = conf->fps; //帧率 if(ioctl(conf->cam_fd,VIDIOC_S_PARM,&fps) ==-1) { fprintf(stderr,"Usage:set_fps:%s error\n",strerror(errno)); exit(EXIT_FAILURE); } } /** * @brief init_mmap * 在内核空间申请帧缓冲、映射到用户空间、并入缓冲队列 * @param fd * @param num */ static void init_mmap(Cam_conf *conf ) { CLEAR(reqbuf); //reqbuf为全局 reqbuf.count = conf->n_buf; reqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; reqbuf.memory = V4L2_MEMORY_MMAP; if(ioctl(conf->cam_fd,VIDIOC_REQBUFS,&reqbuf) == -1) //申请缓存 { fprintf(stderr,"Usage:Init_mmap--VIDIOC_DQBUF:%serror\n",strerror(errno)); exit(EXIT_FAILURE); } if(reqbuf.count < 5) { fprintf(stderr,"Usage:reqbuf.count <5:%s error\n",strerror(errno)); exit(EXIT_FAILURE); } //申请地址 bufs = calloc(reqbuf.count,sizeof(Buf)); //申请内存 assert(bufs != NULL); //入队列 unsigned int i = 0; for(i = 0;i< reqbuf.count;i++) { CLEAR(vbuf); //vbuf为全局变量 vbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; vbuf.memory = V4L2_MEMORY_MMAP; vbuf.index = i; if(ioctl(conf->cam_fd,VIDIOC_QUERYBUF,&vbuf) == -1) //查询,返回内存索引号,获取物理地址 { fprintf(stderr,"Usage:VIDIOC_QUERYBUF:%s error\n",strerror(errno)); exit(EXIT_FAILURE); } bufs[i].len = vbuf.length; bufs[i].start = mmap(NULL, //strat anywhere 内核自动分配 vbuf.length, //由vbuf查询返回 PROT_READ|PROT_WRITE, //required MAP_SHARED, //recommended conf->cam_fd, vbuf.m.offset); //vbuf查询返回,VIDIOC_QUERYBUF if(bufs[i].start == MAP_FAILED) { fprintf(stderr,"Usage:mmap:%s error\n",strerror(errno)); exit(EXIT_FAILURE); } if(ioctl(conf->cam_fd,VIDIOC_QBUF,&vbuf) == -1) //入队 { fprintf(stderr,"Usage:Init_mmap--VIDIOC_QBUF :%s error\n",strerror(errno)); exit(EXIT_FAILURE); } } } /** * @brief cam_free 释放用户缓冲空间 */ static void cam_free(void) { unsigned int i = 0; for(i = 0;i < reqbuf.count;i++) { if(munmap(bufs[i].start,bufs[i].len) == -1) { fprintf(stderr,"Usage:munmap:%s\n",strerror(errno)); exit(EXIT_FAILURE); } } //free(bufs); } /** * @brief start_capturing * 开启视频流 * @param fd */ void start_capturing(int fd) { int type = V4L2_BUF_TYPE_VIDEO_CAPTURE; if(ioctl(fd,VIDIOC_STREAMON,&type) == -1) { fprintf(stderr,"Usage:VIDIOC_STREAMON:%s error\n",strerror(errno)); exit(EXIT_FAILURE); } } /** * @brief stop_capturing * 关闭视频流 * @param fd */ void stop_capturing(int fd) { int type = V4L2_BUF_TYPE_VIDEO_CAPTURE; if(ioctl(fd,VIDIOC_STREAMOFF,&type) == -1) { fprintf(stderr,"Usage:VIDIOC_STREAMOFF:%s error\n",strerror(errno)); exit(EXIT_FAILURE); } } /** * @brief get_frame * 出队一帧数据,拷贝到temp_buf,并入队 * @param conf * @return 成功0,失败-1 */ int get_frame(Cam_conf *conf) { //Vbuf vbuf; CLEAR(vbuf); //一帧图片空间 vbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; vbuf.memory = V4L2_MEMORY_MMAP; //vbuf.index = 0; if(ioctl(conf->cam_fd,VIDIOC_DQBUF,&vbuf) == -1) //取一帧 { fprintf(stderr,"Usage:Get_frame--VIDIOC_DQBUF:%s error\n",strerror(errno)); exit(EXIT_FAILURE); } if(vbuf.bytesused<=HEADERFREAM) { return -1; } switch(conf->fmt) { case MJPEG: memcpy(tmp_buf.start,bufs[vbuf.index].start,vbuf.bytesused); break; case YUYV: // yuv_to_jpeg(bufs[vbuf.index].start,tmp_buf.start,vbuf.bytesused,62); break; default: printf("Unknown Format.\n"); break; } if(ioctl(conf->cam_fd,VIDIOC_QBUF,&vbuf) < 0) { fprintf(stderr,"Usage:Get_frame---VIDIOC_QBUF:%s error\n",strerror(errno)); free(tmp_buf.start); return -1; } return 0; } /** * @brief process * 保存一帧图片到本地目录 * @param conf */ void process(Cam_conf *conf) { int pic_fd = open("webcam.jpeg",O_RDWR|O_CREAT|O_TRUNC,S_IRWXU|S_IRWXG|S_IRWXO); if(pic_fd < 0) { fprintf(stderr,"Usage:pic_fd open:%s error\n",strerror(errno)); exit(EXIT_FAILURE); } get_frame(conf); print_picture(pic_fd,tmp_buf.start,tmp_buf.len); free(tmp_buf.start); close(pic_fd); } /** * @brief cam_init * 查询并设置设备属性、格式、帧率,内存映射设置 * @param fd 摄像头 */ void cam_init(Cam_conf *conf) { set_fmt(conf); set_frame_size(conf); set_fps(conf); init_mmap(conf); } /** * @brief is_huffman * 判断图片是否为huffman编码 * @param buf * @return */ static int is_huffman(unsigned char *buf) { unsigned char *ptbuf; int i = 0; ptbuf = buf; while (((ptbuf[0] << 8) | ptbuf[1]) != 0xffda) { if (i++ > 2048) return 0; if (((ptbuf[0] << 8) | ptbuf[1]) == 0xffc4) return 1; ptbuf++; } return 0; } /** * @brief print_picture * 将图片输出到fd文件中 * @param fd * @param buf * @param size * @return */ int print_picture(int fd, unsigned char *buf, int size) { unsigned char *ptdeb, *ptcur = buf; int sizein; if (!is_huffman(buf)) { ptdeb = ptcur = buf; while (((ptcur[0] << 8) | ptcur[1]) != 0xffc0) ptcur++; sizein = ptcur - ptdeb; if( write(fd, buf, sizein) <= 0) return -1; if( write(fd, dht_data, DHT_SIZE) <= 0) return -1; if( write(fd, ptcur, size - sizein) <= 0) return -1; } else { if( write(fd, ptcur, size) <= 0) return -1; } return 0; } #include "print.h" #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <linux/types.h> #include <string.h> #include <fcntl.h> #include <wait.h> #include <time.h> #include <limits.h> #include "huffman.h" static int is_huffman(unsigned char *buf) { unsigned char *ptbuf; int i = 0; ptbuf = buf; while (((ptbuf[0] << 8) | ptbuf[1]) != 0xffda) { if (i++ > 2048) return 0; if (((ptbuf[0] << 8) | ptbuf[1]) == 0xffc4) return 1; ptbuf++; } return 0; } extern int print_picture(int fd, unsigned char *buf, int size) { unsigned char *ptdeb, *ptcur = buf; int sizein; if (!is_huffman(buf)) { ptdeb = ptcur = buf; while (((ptcur[0] << 8) | ptcur[1]) != 0xffc0) ptcur++; sizein = ptcur - ptdeb; if( write(fd, buf, sizein) <= 0) return -1; if( write(fd, dht_data, DHT_SIZE) <= 0) return -1; if( write(fd, ptcur, size - sizein) <= 0) return -1; } else { if( write(fd, ptcur, size) <= 0) return -1; } return 0; } /* * This file has implements changing YUYV format to * JPEG format */ #include "webcam.h" #include "yuv2jpeg.h" #include <jpeglib.h> #define OUTPUT_BUF_SIZE 4096 METHODDEF(void) init_dst(j_compress_ptr); METHODDEF(boolean) empty_out_buf(j_compress_ptr); METHODDEF(void) term_dst(j_compress_ptr); METHODDEF(void) init_dst(j_compress_ptr cinfo){ jpg_ptr dest = (jpg_ptr)cinfo->dest; dest->buffer = (JOCTET *)(*cinfo->mem->alloc_small)((j_common_ptr)cinfo, JPOOL_IMAGE, OUTPUT_BUF_SIZE * sizeof(JOCTET)); *(dest->written) = 0; dest->jpg_p.next_output_byte = dest->buffer; dest->jpg_p.free_in_buffer = OUTPUT_BUF_SIZE; } METHODDEF(boolean) empty_out_buf(j_compress_ptr cinfo){ jpg_ptr dest = (jpg_ptr)cinfo->dest; memcpy(dest->out_buf_cur, dest->buffer, OUTPUT_BUF_SIZE); dest->out_buf_cur += OUTPUT_BUF_SIZE; *(dest->written) += OUTPUT_BUF_SIZE; dest->jpg_p.next_output_byte = dest->buffer; dest->jpg_p.free_in_buffer = OUTPUT_BUF_SIZE; return TRUE; } METHODDEF(void) term_dst(j_compress_ptr cinfo){ jpg_ptr dest = (jpg_ptr)cinfo->dest; size_t datacount = OUTPUT_BUF_SIZE - dest->jpg_p.free_in_buffer; /* Write any data remaining in the buffer */ memcpy(dest->out_buf_cur, dest->buffer, datacount); dest->out_buf_cur += datacount; *(dest->written) += datacount; } void dst_buffer(j_compress_ptr cinfo, unsigned char *buffer, int size, int *written){ jpg_ptr dest; if (cinfo->dest == NULL) { cinfo->dest = (struct jpeg_destination_mgr *)(*cinfo->mem->alloc_small)((j_common_ptr) cinfo, \ JPOOL_PERMANENT, sizeof(jpg_dst)); } dest = (jpg_ptr)cinfo->dest; dest->jpg_p.init_destination = init_dst; dest->jpg_p.empty_output_buffer = empty_out_buf; dest->jpg_p.term_destination = term_dst; dest->out_buf = buffer; dest->out_buf_size = size; dest->out_buf_cur = buffer; dest->written = written; } //摄像头采集帧图像的YUYV格式转换为JPEG格式 int yuv_to_jpeg(unsigned char *buf, unsigned char *buffer, int size, int quality){ struct jpeg_compress_struct cinfo; struct jpeg_error_mgr jerr; JSAMPROW row_pointer[1]; unsigned char *line_buffer, *yuyv; int z; static int written; //int count = 0; //printf("%s\n", buf); line_buffer = calloc(WIDTH * 3, 1); yuyv = buf;//将YUYV格式的图片数据赋给YUYV指针 cinfo.err = jpeg_std_error(&jerr); jpeg_create_compress(&cinfo); /* jpeg_stdio_dest (&cinfo, file); */ dst_buffer(&cinfo, buffer, size, &written); cinfo.image_width = WIDTH; cinfo.image_height = HEIGHT; cinfo.input_components = 3; cinfo.in_color_space = JCS_RGB; jpeg_set_defaults(&cinfo); jpeg_set_quality(&cinfo, quality, TRUE); jpeg_start_compress(&cinfo, TRUE); z = 0; while(cinfo.next_scanline < HEIGHT){ int x; unsigned char *ptr = line_buffer; for(x = 0; x < WIDTH; x++){ int r, g, b; int y, u, v; if(!z) y = yuyv[0] << 8; else y = yuyv[2] << 8; u = yuyv[1] - 128; v = yuyv[3] - 128; r = (y + (359 * v)) >> 8; g = (y - (88 * u) - (183 * v)) >> 8; b = (y + (454 * u)) >> 8; *(ptr++) = (r > 255) ? 255 : ((r < 0) ? 0 : r); *(ptr++) = (g > 255) ? 255 : ((g < 0) ? 0 : g); *(ptr++) = (b > 255) ? 255 : ((b < 0) ? 0 : b); if(z++) { z = 0; yuyv += 4; } } row_pointer[0] = line_buffer; jpeg_write_scanlines(&cinfo, row_pointer, 1); } jpeg_finish_compress(&cinfo); jpeg_destroy_compress(&cinfo); free(line_buffer); return (written); } |
实现了基本的摄像头这边的硬件初始化配置之后,接下来就是网络这一块,这里需要注意的一是:在我们建立服务器的时候,大概有以下几个步骤:
1、构架socket结构体
2、构件表达服务器的地址结构体
3、像地址结构体设置数据,初始化
4、绑定地址结构体和socket描述符
5、监听客户端的链接
6、准备接收客户端的信息服务
7、启动服务线程,回应请求数据服务,分离线程属性。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 |
/** * @file sock.h * @brief webserver接口【视频传输模块及http协议处理】 * 实现将视频流发送到浏览器上,服务器一次处理一个客户端请求 * 1、先建立浏览器和本服务器的socket通信 * 2、增加http协议,实现发送视频流/图片 */ #include "common.h" typedef struct sockaddr SA; typedef struct sockaddr_in SA_in; #define LISTENQ 1024 /* second argument to listen() */ #define MAX 1024 // 存放接收客户机浏览器请求后的回应:Http head 报文头 static char HEADER[] = "HTTP/1.1 200 OK\r\nConnection: close\r\nCache-Control: no-store, no-cache, must-revalidate, pre-check=0, post-check=0, max-age=0\r\nPragma: no-cache\r\nContent-type: multipart/x-mixed-replace;boundary=www.10086ng.com\r\n\r\n"; SA_in saddr; SA caddr; int socketfd; /** * @brief open_listenfd * 创建并返回监听socket描述付,setsockopt设置IP可重用 * @param port 端口号 * @return 监听socket描述付 */ extern int Open_listenfd(int port); /** * @brief Accept * 等待客户端的链接请求, * @param listenfd 监听socket描述付 * @param addr 客户端地址指针 * @param addrlen 客户端地址长度 * @return 链接socket描述付 */ extern int Accept(int listenfd, SA *addr, socklen_t *addrlen); /** * @brief send_picture * 响应浏览器请求,发送照片或者视频流 * 1、接受浏览器URL信息或客户端信息 * 2、向浏览器响应http 报文头信息 * 3、浏览器请求一张图片 * 4、浏览器请求视频 * @param confd 链接描述付 * @param conf 摄像头配置指针 */ extern void send_picture(int confd, Cam_conf* conf); /** * @brief Client_Info * 打印客户端的IP地址和域名 * @param client_addr */ extern void Client_Info(SA_in *client_addr); /** * @file sock.h * @brief webserver接口【视频传输模块及http协议处理】 * 实现将视频流发送到浏览器上,服务器一次处理一个客户端请求 * 1、先建立浏览器和本服务器的socket通信 * 2、增加http协议,实现发送视频流/图片 */ //typedef struct sockaddr SA; //typedef struct sockaddr_in SA_in; //#define LISTENQ 1024 /* second argument to listen() */ //#define MAX 1024 // 存放接收客户机浏览器请求后的回应:Http head 报文头 //static char HEADER[MAX] = "HTTP/1.1 200 OK\r\nConnection: close\r\nServer: Webcam V0.0\r\nCache-Control: no-store, no-cache,must-revalidate, pre-check=0, post-check=0, max-age=0\r\nPragma:no-cache\r\nContent-type: multipart/x-mixed-replace;boundary=www.10086ng.com\r\n\r\n"; #include "common.h" #include "socket.h" //int socketfd; /** * @brief open_listenfd * 创建并返回监听socket描述符,setsockopt设置IP可重用 * @param port 端口号 * @return 监听socket描述付 */ int Open_listenfd(int port) { socketfd = socket(AF_INET,SOCK_STREAM,0); if(socketfd < 0) { fprintf(stderr,"socketfd create:%s error\n",strerror(errno)); exit(EXIT_FAILURE); } //SA_in saddr; saddr.sin_family = AF_INET; saddr.sin_port = htons(port); saddr.sin_addr.s_addr = INADDR_ANY; bzero(&saddr.sin_zero,8); if(bind(socketfd,(struct sockaddr *)&saddr,sizeof(saddr)) < 0) { fprintf(stderr,"Usage:socketfd bind:%s error\n",strerror(errno)); close(socketfd); exit(EXIT_FAILURE); } if(listen(socketfd,10) < 0) { fprintf(stderr,"Usage:listen:%s error\n",strerror(errno)); exit(EXIT_FAILURE); } printf("NOW SART LISTENING!:%d\n",socketfd); return socketfd; } void *do_service(void *arg) { int confd = (int)arg; print_picture(confd,tmp_buf.start,tmp_buf.len); //send_picture(confd,&conf); } /** * @brief Accept * 等待客户端的链接请求,创建线程 * @param listenfd 监听socket描述付 * @param addr 客户端地址指针 * @param addrlen 客户端地址长度 * @return 链接socket描述付 */ int Accept(int listenfd, SA *addr, socklen_t *addrlen) { int confd; printf("accept!!!\n"); confd = accept(listenfd,addr,addrlen); printf("accept!!!\n"); if(confd < 0) { fprintf(stderr,"Usage:accept:%s error\n",strerror(errno)); close(listenfd); exit(EXIT_FAILURE); } return confd; } /** * @brief send_picture * 响应浏览器请求,发送照片或者视频流 * 1、接受浏览器URL信息或客户端信息 * 2、向浏览器响应http 报文头信息 * 3、浏览器请求一张图片 * 4、浏览器请求视频 * @param confd 链接描述付 * @param conf 摄像头配置指针 */ void send_picture(int confd, Cam_conf* conf) { char buf[1024] = {'\0'}; read(confd,buf,sizeof(buf)); printf("===========\n"); write(1,buf,sizeof(buf)); printf("in client confd =%d\n",confd); write(confd,HEADER,sizeof(HEADER)-1); printf("in client confd =%d\n",confd); printf("=========================================\n"); while(1) { memset(buf,0,sizeof(buf)); sprintf(buf,"--www.10086ng.com\nContent-type: image/jpeg\nContent-Length: %d\n\n",tmp_buf.len+432); write(confd,buf,strlen(buf)); // memcpy(&tmp_buf,buf,tmp_buf.len); //printf("================%s\n",buf); //printf("confd:%d\n",confd); //printf("%s\n",tmp_buf); usleep(100*1000); print_picture(confd,tmp_buf.start,tmp_buf.len); //print_picture(confd,buf,sizeof(buf)); memset(buf,0,sizeof(buf)); sprintf(buf,"\r\n--"); int ok = (write(confd,buf,strlen(buf)) >= 0)?1:0; printf("%d\n",ok); usleep(100*1000); } printf("write picture finished!\n"); pthread_exit((void *)0); /* while(1) { print_picture(confd,tmp_buf.start,tmp_buf.len); printf("while in send picture!\n"); usleep(100*1000); } */ } /** * @brief Client_Info * 打印客户端的IP地址和域名 * @param client_addr */ void Client_Info(SA_in *client_addr) { } |
当写完以上这一块之后,便可以实现基本的服务器这边的架构了,这时我们可以通过浏览器来访问我们的服务器,通过http协议写我们的视屏流到我们的浏览器中,也就是基本的视频已经出来了。
很乱,看个大概就好,实在看不下去的就请拍砖。