zoukankan      html  css  js  c++  java
  • v4l2采集视频和图片源码

      1 #include <stdio.h>
      2 #include <stdlib.h>
      3 #include <string.h>
      4 #include <assert.h>
      5 
      6 #include <getopt.h>             /* getopt_long() */
      7 
      8 #include <fcntl.h>              /* low-level i/o */
      9 #include <unistd.h>
     10 #include <errno.h>
     11 #include <sys/stat.h>
     12 #include <sys/types.h>
     13 #include <sys/time.h>
     14 #include <sys/mman.h>
     15 #include <sys/ioctl.h>
     16 
     17 #include <linux/videodev2.h>
     18 
     19 #define FORCED_WIDTH  640
     20 #define FORCED_HEIGHT 480
     21 #define FORCED_FORMAT V4L2_PIX_FMT_YUYV
     22 #define FORCED_FIELD  V4L2_FIELD_ANY
     23 
     24 static int verbose = 0;
     25 #define pr_debug(fmt, arg...) 
     26     if (verbose) fprintf(stderr, fmt, ##arg)
     27 
     28 #define CLEAR(x) memset(&(x), 0, sizeof(x)) //相当于CLEAR(x) =memset(&(x), 0, sizeof(x))
     29 
     30 enum io_method {                           //枚举,即只能取其中的一个值。也是一种数据结构
     31     IO_METHOD_READ,
     32     IO_METHOD_MMAP,
     33     IO_METHOD_USERPTR,
     34 };
     35 
     36 struct buffer {
     37     void   *start;
     38     size_t  length;                    //size_t =unsigned int 用来表示sizeof的
     39 };
     40 
     41 static char            *dev_name;         //用来保存设备名
     42 static enum io_method   io = IO_METHOD_MMAP;
     43 static int              fd = -1;
     44 struct buffer          *buffers;
     45 static unsigned int     n_buffers;
     46 static int        out_buf;
     47 static int              force_format;
     48 static int              frame_count = 100;
     49 static char             *video_name="video.yuv";
     50     FILE *fp; //保存视频
     51  
     52 
     53 static void errno_exit(const char *s) 
     54 {
     55     fprintf(stderr, "%s error %d, %s
    ", s, errno, strerror(errno));
     56     exit(EXIT_FAILURE);
     57 }
     58 
     59 static int xioctl(int fh, int request, void *arg)
     60 {
     61     int r;
     62 
     63     do {
     64         r = ioctl(fh, request, arg);
     65     } while (-1 == r && EINTR == errno);
     66 
     67     return r;
     68 }
     69 
     70 static void process_image(const void *p, int size)    
     71 {
     72     pr_debug("%s: called!
    ", __func__);
     73          //采集视频      
     74         fwrite(p,size,1,fp);//如果要查看采集视频的格式,在linux下可以通过安装v4l-utils,然后执行v4l2-ctl --all来查看视频采集的相关信息
     75             // 保存为一张张图片
     76       /*     FILE *fp;
     77     static int num = 1;
     78     char picture_name[20];
     79     
     80     sprintf(picture_name,"picture%d.jpg",num ++);
     81     
     82     if((fp = fopen(picture_name,"w")) == NULL)
     83     {
     84         perror("Fail to fopen");
     85         exit(EXIT_FAILURE);
     86     }
     87 
     88     fwrite(p,size,1,fp);
     89     usleep(500);
     90 
     91     fclose(fp);  
     92                                  */
     93 
     94               // 打印到标准输出
     95     /*if (out_buf)
     96         fwrite(p, size, 1, stdout);
     97 
     98     fflush(stderr);
     99     fprintf(stderr, ".");
    100     fflush(stdout);
    101                            */
    102 
    103 
    104 }
    105 
    106 static int read_frame(void)                           
    107 {
    108     struct v4l2_buffer buf;
    109     unsigned int i;
    110 
    111     pr_debug("%s: called!
    ", __func__);
    112 
    113     switch (io) {
    114     case IO_METHOD_READ:
    115         
    116         break;
    117 
    118     case IO_METHOD_MMAP:
    119         CLEAR(buf);
    120 
    121         buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    122         buf.memory = V4L2_MEMORY_MMAP;
    123 
    124         if (-1 == xioctl(fd, VIDIOC_DQBUF, &buf)) {
    125             switch (errno) {
    126             case EAGAIN:
    127                 return 0;
    128 
    129             case EIO:
    130                 /* Could ignore EIO, see spec. */
    131 
    132                 /* fall through */
    133 
    134             default:
    135                 errno_exit("VIDIOC_DQBUF");
    136             }
    137         }
    138 
    139         assert(buf.index < n_buffers);
    140 
    141         process_image(buffers[buf.index].start, buf.bytesused);
    142 
    143         if (-1 == xioctl(fd, VIDIOC_QBUF, &buf))
    144             errno_exit("VIDIOC_QBUF");
    145         break;
    146 
    147     case IO_METHOD_USERPTR:
    148     
    149         break;
    150     }
    151 
    152     return 1;
    153 }
    154 
    155 static void mainloop(void)                             
    156 {
    157     unsigned int count;
    158 
    159     pr_debug("%s: called!
    ", __func__);
    160 
    161     count = frame_count;
    162 
    163     while (count-- > 0) {
    164         for (;;) {
    165             fd_set fds;
    166             struct timeval tv;
    167             int r;
    168 
    169             FD_ZERO(&fds);
    170             FD_SET(fd, &fds);
    171 
    172             /* Timeout. */
    173             tv.tv_sec = 2;
    174             tv.tv_usec = 0;
    175 
    176             r = select(fd + 1, &fds, NULL, NULL, &tv);
    177 
    178             if (-1 == r) {
    179                 if (EINTR == errno)
    180                     continue;
    181                 errno_exit("select");
    182             }
    183 
    184             if (0 == r) {
    185                 fprintf(stderr, "select timeout
    ");
    186                 exit(EXIT_FAILURE);
    187             }
    188 
    189             if (read_frame())
    190                 break;
    191             /* EAGAIN - continue select loop. */
    192         }
    193     }
    194 }
    195 
    196 static void stop_capturing(void)
    197 {
    198     enum v4l2_buf_type type;
    199 
    200     pr_debug("%s: called!
    ", __func__);
    201 
    202     switch (io) {
    203     case IO_METHOD_READ:
    204         /* Nothing to do. */
    205         break;
    206 
    207     case IO_METHOD_MMAP: //会执行下面的
    208     case IO_METHOD_USERPTR:
    209         type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    210         if (-1 == xioctl(fd, VIDIOC_STREAMOFF, &type))
    211             errno_exit("VIDIOC_STREAMOFF");
    212         break;
    213     }
    214 }
    215 
    216 static void start_capturing(void) 
    217 {
    218     unsigned int i;
    219     enum v4l2_buf_type type;
    220     int err;
    221 
    222     pr_debug("%s: called!
    ", __func__);
    223 
    224     pr_debug("	n_buffers: %d
    ", n_buffers);
    225 
    226     switch (io) {
    227     case IO_METHOD_READ:
    228         /* Nothing to do. */
    229         break;
    230 
    231     case IO_METHOD_MMAP:
    232         for (i = 0; i < n_buffers; ++i) {
    233             struct v4l2_buffer buf;
    234 
    235             pr_debug("	i: %d
    ", i);
    236 
    237             CLEAR(buf);
    238             buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    239             buf.memory = V4L2_MEMORY_MMAP;
    240             buf.index = i;
    241 
    242             pr_debug("	buf.index: %d
    ", buf.index);
    243 
    244             err == xioctl(fd, VIDIOC_QBUF, &buf);
    245             pr_debug("	err: %d
    ", err);
    246 
    247             if (-1 == err)
    248                 errno_exit("VIDIOC_QBUF");
    249 
    250             pr_debug("	buffer queued!
    ");
    251         }
    252 
    253         pr_debug("Before STREAMON
    ");
    254         type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    255         if (-1 == xioctl(fd, VIDIOC_STREAMON, &type))
    256             errno_exit("VIDIOC_STREAMON");
    257         pr_debug("After STREAMON
    ");
    258         break;
    259 
    260     case IO_METHOD_USERPTR:
    261         
    262         break;
    263     }
    264 }
    265 
    266 static void uninit_device(void)
    267 {
    268     unsigned int i;
    269 
    270     pr_debug("%s: called!
    ", __func__);
    271 
    272     switch (io) {
    273     case IO_METHOD_READ:
    274         free(buffers[0].start);
    275         break;
    276 
    277     case IO_METHOD_MMAP:
    278         for (i = 0; i < n_buffers; ++i)
    279             if (-1 == munmap(buffers[i].start, buffers[i].length))
    280                 errno_exit("munmap");
    281         break;
    282 
    283     case IO_METHOD_USERPTR:
    284         for (i = 0; i < n_buffers; ++i)
    285             free(buffers[i].start);
    286         break;
    287     }
    288 
    289     free(buffers);
    290 }
    291 
    292 static void init_read(unsigned int buffer_size)
    293 {
    294     pr_debug("%s: called!
    ", __func__);
    295 
    296     buffers = calloc(1, sizeof(*buffers));
    297 
    298     if (!buffers) {
    299         fprintf(stderr, "Out of memory
    ");
    300         exit(EXIT_FAILURE);
    301     }
    302 
    303     buffers[0].length = buffer_size;
    304     buffers[0].start = malloc(buffer_size);
    305 
    306     if (!buffers[0].start) {
    307         fprintf(stderr, "Out of memory
    ");
    308         exit(EXIT_FAILURE);
    309     }
    310 }
    311 
    312 static void init_mmap(void)  //(3)
    313 {
    314     struct v4l2_requestbuffers req;
    315 
    316     pr_debug("%s: called!
    ", __func__);
    317 
    318     CLEAR(req);
    319 
    320     req.count = 4;
    321     req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    322     req.memory = V4L2_MEMORY_MMAP;
    323 
    324     if (-1 == xioctl(fd, VIDIOC_REQBUFS, &req)) {
    325         if (EINVAL == errno) {
    326             fprintf(stderr, "%s does not support "
    327                  "memory mapping
    ", dev_name);
    328             exit(EXIT_FAILURE);
    329         } else {
    330             errno_exit("VIDIOC_REQBUFS");
    331         }
    332     }
    333     pr_debug("	req.count: %d
    ", req.count);
    334     pr_debug("	req.type: %d
    ", req.type);
    335     pr_debug("	req.memory: %d
    ", req.memory);
    336     pr_debug("
    ");
    337 
    338 
    339     if (req.count < 2) {
    340         fprintf(stderr, "Insufficient buffer memory on %s
    ",
    341              dev_name);
    342         exit(EXIT_FAILURE);
    343     }
    344 
    345     buffers = calloc(req.count, sizeof(*buffers));
    346 
    347     if (!buffers) {
    348         fprintf(stderr, "Out of memory
    ");
    349         exit(EXIT_FAILURE);
    350     }
    351 
    352     for (n_buffers = 0; n_buffers < req.count; ++n_buffers) {
    353         struct v4l2_buffer buf;
    354 
    355         CLEAR(buf);
    356 
    357         buf.type        = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    358         buf.memory      = V4L2_MEMORY_MMAP;
    359         buf.index       = n_buffers;
    360 
    361         if (-1 == xioctl(fd, VIDIOC_QUERYBUF, &buf))
    362             errno_exit("VIDIOC_QUERYBUF");
    363 
    364         pr_debug("	buf.index: %d
    ", buf.index);
    365         pr_debug("	buf.type: %d
    ", buf.type);
    366         pr_debug("	buf.bytesused: %d
    ", buf.bytesused);
    367         pr_debug("	buf.flags: %d
    ", buf.flags);
    368         pr_debug("	buf.field: %d
    ", buf.field);
    369         pr_debug("	buf.timestamp.tv_sec: %ld
    ", (long) buf.timestamp.tv_sec);
    370         pr_debug("	buf.timestamp.tv_usec: %ld
    ", (long) buf.timestamp.tv_usec);
    371         pr_debug("	buf.timecode.type: %d
    ", buf.timecode.type);
    372         pr_debug("	buf.timecode.flags: %d
    ", buf.timecode.flags);
    373         pr_debug("	buf.timecode.frames: %d
    ", buf.timecode.frames);
    374         pr_debug("	buf.timecode.seconds: %d
    ", buf.timecode.seconds);
    375         pr_debug("	buf.timecode.minutes: %d
    ", buf.timecode.minutes);
    376         pr_debug("	buf.timecode.hours: %d
    ", buf.timecode.hours);
    377         pr_debug("	buf.timecode.userbits: %d,%d,%d,%d
    ",
    378                 buf.timecode.userbits[0],
    379                 buf.timecode.userbits[1],
    380                 buf.timecode.userbits[2],
    381                 buf.timecode.userbits[3]);
    382         pr_debug("	buf.sequence: %d
    ", buf.sequence);
    383         pr_debug("	buf.memory: %d
    ", buf.memory);
    384         pr_debug("	buf.m.offset: %d
    ", buf.m.offset);
    385         pr_debug("	buf.length: %d
    ", buf.length);
    386         pr_debug("	buf.input: %d
    ", buf.input);
    387         pr_debug("
    ");
    388 
    389         buffers[n_buffers].length = buf.length;
    390         buffers[n_buffers].start =
    391             mmap(NULL /* start anywhere */,
    392                   buf.length,
    393                   PROT_READ | PROT_WRITE /* required */,
    394                   MAP_SHARED /* recommended */,
    395                   fd, buf.m.offset);
    396 
    397         if (MAP_FAILED == buffers[n_buffers].start)
    398             errno_exit("mmap");
    399     }
    400 }
    401 
    402 
    403 static void init_device(void) //static表示次函数只能在本文件中调用  
    404 {
    405     struct v4l2_capability cap;
    406     struct v4l2_cropcap cropcap;
    407     struct v4l2_crop crop;
    408     struct v4l2_format fmt;
    409     unsigned int min;
    410 
    411     pr_debug("%s: called!
    ", __func__);
    412 
    413     if (-1 == xioctl(fd, VIDIOC_QUERYCAP, &cap)) {
    414         if (EINVAL == errno) {
    415             fprintf(stderr, "%s is no V4L2 device
    ",
    416                  dev_name);
    417             exit(EXIT_FAILURE);
    418         } else {
    419             errno_exit("VIDIOC_QUERYCAP");
    420         }
    421     }
    422 
    423     pr_debug("	driver: %s
    "
    424          "	card: %s 
    "
    425          "	bus_info: %s
    ",
    426             cap.driver, cap.card, cap.bus_info);
    427     pr_debug("	version: %u.%u.%u
    ",
    428             (cap.version >> 16) & 0xFF,
    429             (cap.version >> 8) & 0xFF,
    430             cap.version & 0xFF);
    431     pr_debug("	capabilities: 0x%08x
    ", cap.capabilities);
    432 
    433     if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) {
    434         fprintf(stderr, "%s is no video capture device
    ",
    435              dev_name);
    436         exit(EXIT_FAILURE);
    437     }
    438 
    439     
    440 
    441 
    442     /* Select video input, video standard and tune here. */
    443 
    444 
    445     CLEAR(cropcap);
    446 
    447     cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    448 
    449     if (0 == xioctl(fd, VIDIOC_CROPCAP, &cropcap)) {
    450         pr_debug("	cropcap.type: %d
    ", cropcap.type);
    451         pr_debug("	cropcap.bounds.left: %d
    ", cropcap.bounds.left);
    452         pr_debug("	cropcap.bounds.top: %d
    ", cropcap.bounds.top);
    453         pr_debug("	cropcap.bounds. %d
    ", cropcap.bounds.width);
    454         pr_debug("	cropcap.bounds.height: %d
    ", cropcap.bounds.height);
    455 
    456         pr_debug("	cropcap.defrect.left: %d
    ", cropcap.defrect.left);
    457         pr_debug("	cropcap.defrect.top: %d
    ", cropcap.defrect.top);
    458         pr_debug("	cropcap.defrect. %d
    ", cropcap.defrect.width);
    459         pr_debug("	cropcap.defrect.height: %d
    ", cropcap.defrect.height);
    460 
    461         pr_debug("	cropcap.pixelaspect.numerator: %d
    ", cropcap.pixelaspect.numerator);
    462         pr_debug("	cropcap.pixelaspect.denominator: %d
    ", cropcap.pixelaspect.denominator);
    463         pr_debug("
    ");
    464         
    465         CLEAR(crop);
    466         crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    467         crop.c = cropcap.defrect; /* reset to default */
    468 
    469         if (-1 == xioctl(fd, VIDIOC_S_CROP, &crop)) {
    470             switch (errno) {
    471             case EINVAL:
    472                 /* Cropping not supported. */
    473                 break;
    474             default:
    475                 /* Errors ignored. */
    476                 pr_debug("	cropping not supported
    ");
    477                 break;
    478             }
    479         }
    480     } else {
    481         /* Errors ignored. */
    482     }
    483 
    484 
    485     CLEAR(fmt);
    486 
    487     fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    488     if (force_format) {
    489         fmt.fmt.pix.width       = FORCED_WIDTH;
    490         fmt.fmt.pix.height      = FORCED_HEIGHT; 
    491         fmt.fmt.pix.pixelformat = FORCED_FORMAT;
    492         fmt.fmt.pix.field       = FORCED_FIELD;
    493 
    494         pr_debug("	fmt.fmt.pix.pixelformat: %c,%c,%c,%c
    ",
    495                 fmt.fmt.pix.pixelformat & 0xFF,
    496                 (fmt.fmt.pix.pixelformat >> 8) & 0xFF,
    497                 (fmt.fmt.pix.pixelformat >> 16) & 0xFF,
    498                 (fmt.fmt.pix.pixelformat >> 24) & 0xFF
    499                 );
    500         pr_debug("
    ");
    501 
    502         if (-1 == xioctl(fd, VIDIOC_S_FMT, &fmt))
    503             errno_exit("VIDIOC_S_FMT");
    504 
    505         /* Note VIDIOC_S_FMT may change width and height. */
    506     } else {
    507         /* Preserve original settings as set by v4l2-ctl for example */
    508         if (-1 == xioctl(fd, VIDIOC_G_FMT, &fmt))
    509             errno_exit("VIDIOC_G_FMT");
    510 
    511         fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    512         pr_debug("	fmt.fmt.pix.pixelformat: %c,%c,%c,%c
    ",
    513                 fmt.fmt.pix.pixelformat & 0xFF,
    514                 (fmt.fmt.pix.pixelformat >> 8) & 0xFF,
    515                 (fmt.fmt.pix.pixelformat >> 16) & 0xFF,
    516                 (fmt.fmt.pix.pixelformat >> 24) & 0xFF
    517                 );
    518 
    519     }
    520 
    521     /* Buggy driver paranoia. */
    522     min = fmt.fmt.pix.width * 2;
    523     if (fmt.fmt.pix.bytesperline < min)
    524         fmt.fmt.pix.bytesperline = min;
    525     min = fmt.fmt.pix.bytesperline * fmt.fmt.pix.height;
    526     if (fmt.fmt.pix.sizeimage < min)
    527         fmt.fmt.pix.sizeimage = min;
    528 
    529     switch (io) {
    530     case IO_METHOD_READ:
    531         init_read(fmt.fmt.pix.sizeimage);
    532         break;
    533 
    534     case IO_METHOD_MMAP:
    535         init_mmap();
    536         break;
    537 
    538     case IO_METHOD_USERPTR:
    539         
    540         break;
    541     }
    542 }
    543 
    544 static void close_device(void)
    545 {
    546     pr_debug("%s: called!
    ", __func__);
    547 
    548     if (-1 == close(fd))//==可以防止你写成=(相当与赋值)这样条件始终成立
    549         errno_exit("close");
    550 
    551     fd = -1;
    552 }
    553 
    554 static void open_device(void)//
    555 {
    556     struct stat st;
    557 
    558     pr_debug("%s: called!
    ", __func__);//_func_表示此时在那个函数里面
    559 
    560     if (-1 == stat(dev_name, &st)) {//获取文件信息
    561         fprintf(stderr, "Cannot identify '%s': %d, %s
    ",
    562              dev_name, errno, strerror(errno));
    563         exit(EXIT_FAILURE);//非正常退出 EXIT_FAILURE=1, EXIT_SUCCSES=0 正常退出
    564     }
    565 
    566     if (!S_ISCHR(st.st_mode)) {
    567         fprintf(stderr, "%s is no device
    ", dev_name);
    568         exit(EXIT_FAILURE);
    569     }
    570 
    571     fd = open(dev_name, O_RDWR /* required */ | O_NONBLOCK, 0);//非阻塞模式打开文件
    572 
    573     if (-1 == fd) {
    574         fprintf(stderr, "Cannot open '%s': %d, %s
    ",
    575              dev_name, errno, strerror(errno));
    576         exit(EXIT_FAILURE);
    577     }
    578 }
    579 
    580 static void usage(FILE *fp, int argc, char **argv)
    581 {
    582     fprintf(fp,
    583          "Usage: %s [options]
    
    "
    584          "Version 1.3
    "
    585          "Options:
    "
    586          "-d | --device name   Video device name [%s]
    "
    587          "-h | --help          Print this message
    "
    588          "-m | --mmap          Use memory mapped buffers [default]
    "
    589          "-r | --read          Use read() calls
    "
    590          "-u | --userp         Use application allocated buffers
    "
    591          "-o | --output        Outputs stream to stdout
    "
    592          "-f | --format        Force format to 640x480 YUYV
    "
    593          "-c | --count         Number of frames to grab [%i]
    "
    594          "-v | --verbose       Verbose output
    "
    595          "",
    596          argv[0], dev_name, frame_count);
    597 }
    598 //关于命令解析见百度getopt-long;
    599 static const char short_options[] = "d:hmruofc:v";// 短选项 :表示带参数
    600 
    601 static const struct option //长选项
    602 long_options[] = {
    603     { "device", required_argument, NULL, 'd' },
    604     { "help",   no_argument,       NULL, 'h' },
    605     { "mmap",   no_argument,       NULL, 'm' },
    606     { "read",   no_argument,       NULL, 'r' },
    607     { "userp",  no_argument,       NULL, 'u' },
    608     { "output", no_argument,       NULL, 'o' },
    609     { "format", no_argument,       NULL, 'f' },
    610     { "count",  required_argument, NULL, 'c' },
    611     { "verbose", no_argument,      NULL, 'v' },
    612     { 0, 0, 0, 0 }
    613 };
    614 
    615 int main(int argc, char **argv)
    616 {
    617     dev_name = "/dev/video0";
    618 
    619     for (;;) {
    620         int idx;
    621         int c;
    622 
    623         c = getopt_long(argc, argv,
    624                 short_options, long_options, &idx);
    625 
    626         if (-1 == c)
    627             break;
    628 
    629         switch (c) {
    630         case 0: /* getopt_long() flag */
    631             break;
    632 
    633         case 'd':
    634             dev_name = optarg;
    635             break;
    636 
    637         case 'h':
    638             usage(stdout, argc, argv);
    639             exit(EXIT_SUCCESS);
    640 
    641         case 'm':
    642             io = IO_METHOD_MMAP;
    643             break;
    644 
    645         case 'r':
    646             io = IO_METHOD_READ;
    647             break;
    648 
    649         case 'u':
    650             io = IO_METHOD_USERPTR;
    651             break;
    652 
    653         case 'o':
    654             out_buf++;
    655             break;
    656 
    657         case 'f':
    658             force_format++;
    659             break;
    660 
    661         case 'c':
    662             errno = 0;
    663             frame_count = strtol(optarg, NULL, 0);
    664             if (errno)
    665                 errno_exit(optarg);
    666             break;
    667 
    668         case 'v':
    669             verbose = 1;
    670             break;
    671 
    672         default:
    673             usage(stderr, argc, argv);
    674             exit(EXIT_FAILURE);
    675         }
    676     }
    677 
    678 if((fp = fopen(video_name,"wa+")) == NULL)
    679     {
    680         perror("Fail to fopen");
    681         exit(EXIT_FAILURE);
    682     }
    683       
    684     open_device();
    685     init_device();
    686     start_capturing();
    687     mainloop();
    688     stop_capturing();
    689     uninit_device();
    690     close_device();
    691     fprintf(stderr, "
    ");
    692     return 0;
    693 }
  • 相关阅读:
    NOIP2016-2020 复盘
    「笔记」线段树合并/分裂
    「笔记」线性基
    20210628模拟赛解题报告
    「笔记」左偏树
    题解 CF718C Sasha and Array
    一些杂碎的知识点
    20210614 模拟赛
    洛谷 P4249 [WC2007]剪刀石头布
    CF132E Bits of merry old England
  • 原文地址:https://www.cnblogs.com/sj-lv/p/3445056.html
Copyright © 2011-2022 走看看