Chinaunix首页 | 论坛 | 博客
  • 博客访问: 41607
  • 博文数量: 21
  • 博客积分: 840
  • 博客等级: 准尉
  • 技术积分: 225
  • 用 户 组: 普通用户
  • 注册时间: 2010-05-24 00:24
文章分类
文章存档

2010年(21)

我的朋友

分类: LINUX

2010-06-08 21:28:08

同样是v4l2测试程序,但这个版本全很多,忘记在哪转过来的了。
 

/*head files:*/
#include <STDIO.H>
#include <STDLIB.H>
#include <STRING.H>
#include <ASSERT.H>
  
#include <GETOPT.H>
   
#include <FCNTL.H>
#include <UNISTD.H>
#include <ERRNO.H>
#include <SIGNAL.H>
  
   
#include <SYS stat.h>
#include <SYS types.h>
#include <SYS time.h>
#include <SYS mman.h>
#include <SYS ioctl.h>
   
#include <ASM types.h>
   
#include <LINUX videodev2.h>
  
//////////////////////////////////////////declarations:

//state reset:

#define CLEAR(x) memset(&(x),0,sizeof(x))
//devices:

#define V4L2_DEVICE "/dev/video0"
#define FBVID_DEVICE "/dev/fb/3"
#define OUTPUTFILE "/mnt/gjf/vidrecord.uyvy"
  
///////////////////////////////////////////////////////////////////////////////////////////

#define SCREEN_BPP 16
#define D1_WIDTH 352
#define D1_HEIGHT 288
#define D1_LINE_WIDTH (D1_WIDTH * SCREEN_BPP / 8)
#define D1_FRAME_SIZE (D1_LINE_WIDTH * D1_HEIGHT)
  
////////////////////////////////////////////////////////////////////////////////////////////

typedef enum{
    IO_METHOD_READ,
    IO_METHOD_MMAP,
    IO_METHOD_USERPTR
}io_method;
  
struct buffer{
     void* start;
      size_t length;
};
  
static char* video_device=NULL;
static char* output_device=NULL;
static io_method io=IO_METHOD_MMAP;
struct buffer* buffers=NULL;
static unsigned int n_buffers=0;
static int videofd = -1 ; //device discription for video device;

static int outputfd = -1 ; //device discprition for output device;

static int captureSize = 0 ; //picture size

static unsigned int imagewidth = 0 ; //image width

static unsigned int imageheight = 0 ; //image height

static unsigned int timelimit = 1 ; //capturing time

static unsigned int totalframes = 0 ; //catpturing frames

  
#define FRAME 0
#define TIME 1
static int flag = FRAME;
  
////////////////////////////////////////////////////////

typedef enum{YUYV=0,UYVY}imageType;
static imageType format=YUYV;
///////////////////////////////////////////////////////

  
//command line:

static const char short_options[]="d:pmruw:h:t:f:o:";
  
static const struct option long_options[] =
{
     {"device",required_argument,NULL,'d'},
     {"help",no_argument,NULL,'p'},
     {"mmap",no_argument,NULL,'m'},
     {"read",no_argument,NULL,'r'},
     {"usrptr",no_argument,NULL,'u'},
     {"width",required_argument,NULL,'w'},
     {"height",required_argument,NULL,'h'},
     {"time",required_argument,NULL,'t'},
     {"frame",required_argument,NULL,'f'},
      {"format",required_argument,NULL,'o'},
      { 0 , 0 , 0 , 0 }
};
  
static void usage(FILE* fp,int argc,char **argv)
{
  fprintf (fp,"Usage: %s [options]\n\n"
              "Options:\n"
              "-d | --device name Video device name [/dev/video]\n"
              "-p | --help Print this message\n"
              "-m | --mmap Use memory mapped buffers\n"
              "-r | --read Use read() calls\n"
              "-u | --userp Use application allocated buffers\n"
              "-w | --image width\n"
              "-h | --image height\n"
              "-t | --capture time\n"
              "-f | --number of frames\n"
              "-o | --image format\n"
                "",
             argv[0]);
}
  
  
void process_command(int argc,char **argv)
{
     int index ;
     int next_option ;
     int f = -1;
     video_device = V4L2_DEVICE ;
     output_device = FBVID_DEVICE ;
     imagewidth = D1_WIDTH ;
     imageheight = D1_HEIGHT ;
     timelimit = 1 ;
     totalframes = 0 ;
     format = UYVY ;
  
     flag = FRAME ;
      
     do{
         next_option = getopt_long(argc,argv,short_options,long_options,&index) ;
         switch(next_option) {
             case 'd' :
                  video_device=optarg ;
                  break;
             case 'p' :
                  usage(stdout,argc,argv) ;
                  exit(EXIT_FAILURE) ;
             case 'm':
                  io = IO_METHOD_MMAP ;
                  break ;
             case 'r':
                  io = IO_METHOD_READ ;
                  break ;
             case 'u':
                  io = IO_METHOD_USERPTR ;
                  break ;
             case 'w':
                  imagewidth = atoi(optarg);
                  break;
             case 'h':
                  imageheight = atoi(optarg);
                  break;
             case 't':
                  flag = TIME;
                  timelimit = atoi( optarg ) ;
                  break;
             case 'f':
                  flag = FRAME;
                  totalframes = atoi( optarg ) ;
                  break;
             case 'o':
                  f = atoi(optarg);
                  format = ( f >= YUYV && f <= UYVY )? f : UYVY;
                  break;
             case -1:
                  break ;
             default:
                  usage(stdout,argc,argv) ;
                  exit(EXIT_FAILURE) ;
            
           }
  
      }while(next_option!=-1);
}
  
  
///////////////////videoCapture:

void open_device()
{
     struct stat st;
  
     if (-1 == stat (video_device, &st)) {
         fprintf (stderr, "Cannot identify ’%s’: %d, %s\n",video_device, errno, strerror (errno));
         exit (EXIT_FAILURE);
      }
  
     if (!S_ISCHR (st.st_mode)) {
         fprintf (stderr, "%s is no device\n", video_device);
         exit (EXIT_FAILURE);
      }
  
     videofd = open (video_device, O_RDWR | O_NONBLOCK, 0);
     if (videofd == -1) {
         fprintf ( stderr, "Cannot open ’%s’: %d, %s\n", video_device , errno , strerror (errno) );
         exit (EXIT_FAILURE);
      }
}
  
  
static void init_read(unsigned int bufSize) //directly read

{
   buffers = calloc (1, sizeof (*buffers));
   if (!buffers) {
       fprintf (stderr, "Out of memory\n");
       exit (EXIT_FAILURE);
    }
    buffers[0].length = bufSize ;
    buffers[0].start = malloc (bufSize);
    if (!buffers[0].start) {
          fprintf (stderr, "Out of memory\n");
          exit (EXIT_FAILURE);
    }
}
  
static void init_mmap() //map the driver's buffer to application buffer

{
   struct v4l2_requestbuffers req;
   CLEAR (req);
   req.count = 4;
   req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
   req.memory = V4L2_MEMORY_MMAP;
   if (ioctl(videofd, VIDIOC_REQBUFS, &req) == -1) { //allocate buffer in the driver memory space

      if (EINVAL == errno) {
          fprintf (stderr, "%s does not support ""memory mapping\n", video_device);
          exit (EXIT_FAILURE);
      } else {
          fprintf(stderr,"Failed:VIDIOC_REQBUFS");
          exit (EXIT_FAILURE);
       }
    }
    
   if (req.count < 2) { //double buffers

      fprintf (stderr, "Insufficient buffer memory on %s\n", video_device);
      exit (EXIT_FAILURE);
    }
  
   buffers = calloc (req.count, sizeof (*buffers));
   if (!buffers) {
       fprintf (stderr, "Out of memory\n");
       exit (EXIT_FAILURE);
    }
   for (n_buffers = 0; n_buffers < req.count; ++n_buffers) {
  
       struct v4l2_buffer buf;
       CLEAR (buf);
       buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
       buf.memory = V4L2_MEMORY_MMAP;
       buf.index = n_buffers;
  
       if (-1 == ioctl (videofd, VIDIOC_QUERYBUF, &buf)) { //query the state of one of the buffers in driver space

            fprintf(stderr,"Failed:VIDIOC_QUERYBUF ");
            exit(EXIT_FAILURE);
        
        }
  
       buffers[n_buffers].length = buf.length;
  
         
  
       buffers[n_buffers].start = mmap ( NULL /* start anywhere */,
                            buf.length,
                            PROT_READ | PROT_WRITE /* required */,
                            MAP_SHARED /* recommended */,
                            videofd, buf.m.offset);
  
    if (buffers[n_buffers].start == MAP_FAILED ) {
            fprintf(stderr,"Failed:when MMAP");
            exit(EXIT_FAILURE);
  
         }
     }
  
}
  
static void init_usrptr(unsigned int bufSize) //

{
   struct v4l2_requestbuffers req;
   CLEAR (req);
   req.count = 4;
   req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
   req.memory = V4L2_MEMORY_USERPTR;
   if (-1 == ioctl (videofd, VIDIOC_REQBUFS, &req)) {
       if (EINVAL == errno) {
            fprintf (stderr, "%s does not support ""user pointer i/o\n", video_device);
            exit (EXIT_FAILURE);
        }
       else {
            fprintf(stderr,"Failed:VIDIOC_REQBUFS\n");
        }
    }
   buffers = calloc (4, sizeof (*buffers));
   if (!buffers) {
       fprintf (stderr, "Out of memory\n");
       exit (EXIT_FAILURE);
    }
   for (n_buffers = 0; n_buffers < 4; ++n_buffers) {
       buffers[n_buffers].length = bufSize;
       buffers[n_buffers].start = malloc (bufSize); //allocate buffers in user space

       if (!buffers[n_buffers].start) {
            fprintf (stderr, "Out of memory\n");
            exit (EXIT_FAILURE);
        }
    }
   
}
  
static int xioctl( int fd,int request,void * arg)
{
    int r;
    do{
         r = ioctl (fd, request, arg);
     }
    while (-1 == r && EINTR == errno);
    return r;
}
  
#define VPFE_STD_AUTO ((v4l2_std_id)(0x1000000000000000ULL))
  
  
void init_device()
{
    struct v4l2_capability cap;
    struct v4l2_cropcap cropCap;
    struct v4l2_crop crop;
    struct v4l2_format fmt;
     unsigned int min;
     //get the capture device's capabilities

    if (ioctl(videofd, VIDIOC_QUERYCAP, &cap) == -1) {
       if (errno == EINVAL) {
            fprintf ( stderr, "%s is no V4L2 device\n",video_device);
            exit(EXIT_FAILURE);
        }
       fprintf(stderr,"Failed VIDIOC_QUERYCAP on %s (%s)\n", video_device,strerror(errno));
       exit(EXIT_FAILURE);
    }
  
   if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) {
       fprintf(stderr,"%s is no video capture device\n", video_device);
       exit(EXIT_FAILURE);
    }
                  
   switch (io) {
      case IO_METHOD_READ:
           if (!(cap.capabilities & V4L2_CAP_READWRITE)) {
              fprintf (stderr, "%s does not support read i/o\n",video_device);
              exit (EXIT_FAILURE);
            }
       break;
      case IO_METHOD_MMAP:
      case IO_METHOD_USERPTR:
       if (!(cap.capabilities & V4L2_CAP_STREAMING)) {
            fprintf (stderr, "%s does not support streaming i/o\n",video_device);
            exit (EXIT_FAILURE);
            }
    }
  
    //set croping and scaling attribure

    CLEAR(cropCap);
    cropCap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    if (0 == ioctl (videofd, VIDIOC_CROPCAP, &cropCap)) {
         crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
         crop.c = cropCap.defrect; /* reset to default */
         if (-1 == ioctl (videofd, VIDIOC_S_CROP, &crop)) {
              switch (errno) {
                 case EINVAL:
                    fprintf(stderr,"Cropping not supported\n");
                    //exit (EXIT_FAILURE);

                 default:
                    fprintf(stderr, "Failed:VIDIOC_S_CROP");
                   // exit (EXIT_FAILURE);

                 }
           }
     }
    else {
         fprintf(stderr,"Failed:VIDIOC_CROPCAP");
         //exit(EXIT_FAILURE);

    }
  
  
  
     //set picture format

    fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    fmt.fmt.pix.width = D1_WIDTH; //720

    fmt.fmt.pix.height = D1_HEIGHT; //480

     
    switch(format) {
        case YUYV :
             fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
             break;
        case UYVY :
             fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_UYVY;
             break;
        default :
             fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_UYVY;
    }
  
    //fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;

    fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;
  
    if (-1 == ioctl (videofd, VIDIOC_S_FMT, &fmt)){
       fprintf(stderr,"Failed: VIDIOC_S_FMT");
       exit(EXIT_FAILURE);
     }
  
    captureSize = fmt.fmt.pix.sizeimage;
/*
   min = fmt.fmt.pix.width * 2;
   if (fmt.fmt.pix.bytesperline < min)
      fmt.fmt.pix.bytesperline = min;
   min = fmt.fmt.pix.bytesperline * fmt.fmt.pix.height;
   if (fmt.fmt.pix.sizeimage < min)
       fmt.fmt.pix.sizeimage=min;
   fprintf(stderr,"fmt size : %u \n",fmt.fmt.pix.sizeimage);
  
*/

  
 // printf("%width:%u,height:%u,sizeimage:%u\n",fmt.fmt.pix.width,fmt.fmt.pix.height,fmt.fmt.pix.sizeimage);

   //allocate buffer:

   switch(io){
        case IO_METHOD_READ:
             init_read(fmt.fmt.pix.sizeimage);
             break;
        case IO_METHOD_MMAP:
             init_mmap();
             break;
        case IO_METHOD_USERPTR:
             init_usrptr(fmt.fmt.pix.sizeimage);
             break;
        default:
              fprintf(stderr,"IO_METHOD : No such method\n");
             break;
  
     }
  
}
  
  
void startCapturing(void)
{
    unsigned int i;
    enum v4l2_buf_type type;
    switch (io) {
        case IO_METHOD_READ:
             /* Nothing to do. */
             break;
        case IO_METHOD_MMAP:
             for (i = 0; i < n_buffers; ++i) {
   
                struct v4l2_buffer buf;
   
                CLEAR (buf);
   
                buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
                buf.memory = V4L2_MEMORY_MMAP;
                buf.index = i;
                if (-1 == xioctl (videofd, VIDIOC_QBUF, &buf)) { //enqueue an empty capturing buffer allocated in

                                                                   //driver space

                      fprintf(stderr,"Failed(MMAP):VIDIOC_QBUF\n");
                      exit(EXIT_FAILURE);
                }
              }
  
              type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
              if (-1 == ioctl (videofd, VIDIOC_STREAMON, &type)){ //start capturing

                       fprintf(stderr,"Failed(MMAP):VIDIOC_STREAMON\n");
                       exit(EXIT_FAILURE);
              }
              break;
        case IO_METHOD_USERPTR:
             for (i = 0; i < n_buffers; ++i) {
                 struct v4l2_buffer buf;
                 CLEAR (buf);
                 buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
                 buf.memory = V4L2_MEMORY_USERPTR;
                 buf.m.userptr = (unsigned long) buffers[i].start;
                 buf.length = buffers[i].length;
  
                 if (-1 == ioctl (videofd, VIDIOC_QBUF, &buf)) { //encode an empty buffer allocated in usr space

                                                                   // in the capturing queue

                     fprintf(stderr,"Failed(USERPTR):VIDIOC_QBUF\n");
                     exit(EXIT_FAILURE);
                 }
              }
  
              type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
              if (-1 == ioctl (videofd, VIDIOC_STREAMON, &type)) { //start capturing

                     fprintf(stderr,"Failed(USERPTR):VIDIOC_STREAMON\n");
                     exit(EXIT_FAILURE);
              }
     }
  
}
  
static int process_image(const void *p)
{
       
         
  
  
     return 0;
}
  
static int read_frame (void)
{
    struct v4l2_buffer buf;
    unsigned int i;
    switch (io) {
        case IO_METHOD_READ:
           if (-1 == read (videofd, buffers[0].start, buffers[0].length)) { //read data into the usr buffer,

                                                                             //note there is only one buffer

              switch (errno) {
                 case EAGAIN:
                    return 0;
                 case EIO:
                   /* Could ignore EIO, see spec. */
                   /* fall through */
                 default:
                    {
                    fprintf(stderr,"Failed : read");
                    exit(EXIT_FAILURE);
                    }
                }
             }
  
  
       // if (write(outputfd, buffers[buf.index].start , captureSize ) == -1) //write to the output file

        // fprintf(stderr,"Error writing the data to output file\n");

  
           process_image (buffers[0].start); //process this buffer

           break;
       case IO_METHOD_MMAP:
           CLEAR (buf);
           buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
           buf.memory = V4L2_MEMORY_MMAP;
           if (-1 == xioctl (videofd, VIDIOC_DQBUF, &buf)) { //dequeue a buffer

               switch (errno) {
                   case EAGAIN:
                        return 0;
                   case EIO:
                   /* Could ignore EIO, see spec. */
                   /* fall through */
                   default:
                      {
                       fprintf(stderr,"Failed : VIDIOC_DQBUF");
                       exit(EXIT_FAILURE);
                      }
                }
            }
           assert (buf.index < n_buffers);
            
  
  
      // if (write(outputfd, buffers[buf.index].start , captureSize ) == -1);

  
           process_image (buffers[buf.index].start); //process this buffer

           if (-1 == xioctl (videofd, VIDIOC_QBUF, &buf)) { //enqueue this buffer in order to reuse it

                   fprintf(stderr,"Failed : VIDIOC_QBUF\n");
                   exit(EXIT_FAILURE);
           }
           break;
        case IO_METHOD_USERPTR:
           CLEAR (buf);
           buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
           buf.memory = V4L2_MEMORY_USERPTR;
           if (-1 == xioctl (videofd, VIDIOC_DQBUF, &buf)) {
               switch (errno) {
                  case EAGAIN:
                     return 0;
                  case EIO:
                  /* Could ignore EIO, see spec. */
                  /* fall through */
                  default:
                     {
                      fprintf(stderr,"Failed : VIDIOC_DQBUF");
                      exit(EXIT_FAILURE);
                     }
                 }
              }
            for (i = 0; i < n_buffers; ++i) //find the dequeued buffer in usr space

               if (buf.m.userptr == (unsigned long) buffers[i].start && buf.length == buffers[i].length)
                    break;
            assert (i < n_buffers);
  
  // if (write(outputfd, buffers[i].start , captureSize ) == -1) //write to the output file

   // fprintf(stderr,"Error writing the data to output file\n");

  
            process_image ((void *) buf.m.userptr);
            if (-1 == xioctl (videofd, VIDIOC_QBUF, &buf)) { //reuse

                  fprintf(stderr,"Failed : VIDIOC_QBUF");
                  exit(EXIT_FAILURE);
              }
            break;
    }
   return 0;
}
  
  
  
///////////////////////////////////////////////////////////////////////////////////////////////////////

void timehandle(int signo)
{
   fprintf(stderr,"EXIT : time over !--->%u\n",timelimit);
   exit(0);
}
  
void init_sigaction(void)
{
  struct sigaction act;
  act.sa_handler=timehandle;
  act.sa_flags=0;
  sigemptyset(&act.sa_mask);
  sigaction(SIGPROF,&act,NULL);
}
  
void init_time()
{
  struct itimerval value;
  value.it_value.tv_sec= 1;
  value.it_value.tv_usec=0;
  value.it_interval=value.it_value;
  setitimer(ITIMER_PROF,&value,NULL);
}
  
  
///////////////////////////////////////////////////////////////////////////////////////////////////////

  
void mainloop (void)
{
   static int framecount=0;
   
     
   outputfd = open(OUTPUTFILE , O_WRONLY | O_CREAT | O_TRUNC, 00644);
   if (outputfd == -1) {
        fprintf(stderr,"Failed to open %s for writing\n", OUTPUTFILE);
        exit(EXIT_FAILURE);
    }
     
    if(flag == TIME)
     {
       //init_sigaction( ) ;

       //init_time( ) ;

       while( 1 ){
          fd_set fds;
          struct timeval tv;
          int r;
          FD_ZERO (&fds);
          FD_SET (videofd, &fds);
          /* Timeout. */
          tv.tv_sec = 2;
          tv.tv_usec = 0;
          r = select (videofd + 1, &fds, NULL, NULL, &tv); //wait for a new frame

          if (-1 == r) {
             if (EINTR == errno)
                 continue;
             fprintf(stderr,"select");
             exit(EXIT_FAILURE);
           }
          if (0 == r) {
             fprintf (stderr, "select timeout\n");
             exit (EXIT_FAILURE);
           }
  
          if (read_frame ())
             break;
          printf("capturing frame %d \n",framecount);
          framecount++;
  
          /* EAGAIN - continue select loop. */
        }
    }
   else
       while( framecount < totalframes ){
          fd_set fds;
          struct timeval tv;
          int r;
          FD_ZERO (&fds);
          FD_SET (videofd, &fds);
          /* Timeout. */
          tv.tv_sec = 2;
          tv.tv_usec = 0;
         // fprintf(stderr,"wait for a new frame\n");

          r = select (videofd + 1, &fds, NULL, NULL, &tv); //wait for a new frame

          if (-1 == r) {
             if (EINTR == errno)
                 continue;
             fprintf(stderr,"select");
             exit(EXIT_FAILURE);
           }
          if (0 == r) {
             fprintf (stderr, "select timeout\n");
             exit (EXIT_FAILURE);
           }
  
          //fprintf(stderr,"Read next frame :\n");

          if (read_frame ())
             break;
          printf("capturing frame %d \n",framecount);
          framecount++;
  
          /* EAGAIN - continue select loop. */
       }
   if( outputfd != -1 )
      close(outputfd);
}
  
  
//---------------------------------------------

void stop_capturing (void) //stop catpureing pictures

{
  enum v4l2_buf_type type;
  switch (io) {
     case IO_METHOD_READ:
      /* Nothing to do. */
           break;
     case IO_METHOD_MMAP:
     case IO_METHOD_USERPTR:
         type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
         if (-1 == xioctl (videofd, VIDIOC_STREAMOFF, &type)){
             fprintf (stderr,"VIDIOC_STREAMOFF");
             exit(EXIT_FAILURE);
          }
         break;
}
  
void uninit(void)
{
     unsigned int i;
     switch (io) {
        case IO_METHOD_READ:
           free (buffers[0].start);
           break;
        case IO_METHOD_MMAP:
           for (i = 0; i < n_buffers; ++i)
               if (-1 == munmap (buffers[i].start, buffers[i].length)){
                  fprintf(stderr,"munmap");
                  exit(EXIT_FAILURE);
                 }
           break;
        case IO_METHOD_USERPTR:
           for (i = 0; i < n_buffers; ++i)
               free (buffers[i].start);
           break;
          }
        free (buffers);
     }
}
  
void close_device (void)
{
    if (-1 == close (videofd))
     {
       fprintf(stderr,"close");
       exit(EXIT_FAILURE);
     }
    videofd = -1;
}
  
///////////////////////////////////////////////////////////////////////////entry:

  
int main(int argc,char **argv)
{
  process_command(argc,argv);
  open_device();
  fprintf(stderr,"SUCCESS : open_device \n");
  init_device();
  fprintf(stderr,"SUCCESS : Init_device \n");
  startCapturing();
  fprintf(stderr,"SUCCESS : startCapturing\n");
   
  mainloop();
  fprintf(stderr,"SUCCESS : stopCapturing\n");
  stop_capturing() ;
  fprintf(stderr,"SUCCUSS : closeDevice\n");
  close_device();
   
  return 0;
}


阅读(800) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~