Video4linux2(简称V4L2),是Linux中关于视频设备的内核驱动。

V4L2较V4L有较大的改动,并已成为2.6的标准接口,函盖video\dvb\FM...,多数驱动都在向V4l2迁移。更好地了解V4L2先从应用入手,然后再深入到内核中结合物理设备/接口的规范实现相应的驱动。V4L2采用流水线的方式,操作更简单直观,基本遵循打开视频设备、设置格式、处理数据、关闭设备,更多的具体操作通过ioctl函数来实现。

在Linux中,视频设备是设备文件,可以像访问普通文件一样对其进行读写,摄像头在/dev/video0下。

操作系统一般把系统使用的内存划分成用户空间和内核空间,分别由应用程序管理和操作系统管理。应用程序可以直接访问内存的地址,而内核空间存放的是供内核访问的代码和数据,用户不能直接访问。v4l2捕获的数据,最初是存放在内核空间的,这意味着用户不能直接访问该段内存,必须通过某些手段来转换地址。

v4l2一共有三种视频采集方式:使用read、write方式;内存映射方式和用户指针模式。

read、write方式,在用户空间和内核空间不断拷贝数据,占用了大量用户内存空间,效率不高。

内存映射方式:把设备里的内存映射到应用程序中的内存控件,直接处理设备内存,这是一种有效的方式。上面的mmap函数就是使用这种方式。

用户指针模式:内存片段由应用程序自己分配。这点需要在v4l2_requestbuffers里将memory字段设置成V4L2_MEMORY_USERPTR。

注意:

利用v4l2采集时,ARM上的采集卡支持3种模式,普通usb摄像头不支持直接读取模式;
采集卡支持V4L2_PIX_FMT_YUYV和V4L2_PIX_FMT_YUV420;

但是普通usb摄像头不支持V4L2_PIX_FMT_YUV420

captrue(直接读取方式):

代码如下:

#include <stdio.h>    
#include <stdlib.h>    
#include <string.h>    
#include <assert.h>    
#include <getopt.h>             /* getopt_long() */    
#include <fcntl.h>              /* low-level i/o */    
#include <unistd.h>    
#include <errno.h>    
#include <malloc.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>          /* for videodev2.h */    
#include <linux/videodev2.h>

#define CODEC_NODE  "/dev/video0"
#define LCD_WIDTH  320  
#define LCD_HEIGHT 240 
#define YUV_FRAME_BUFFER_SIZE (LCD_WIDTH*LCD_HEIGHT)+(LCD_WIDTH*LCD_HEIGHT)/2  /* YCBCR 420 */

static int cam_c_init(void);
static void exit_from_app() ;
static int cam_c_fp = -1;

static void exit_from_app() 
{
  close(cam_c_fp);
 
}

static int cam_c_init(void)  
{
 int dev_fp = -1;

dev_fp = open(CODEC_NODE, O_RDWR);

if (dev_fp < 0) {
  perror(CODEC_NODE);
  printf("CODEC : Open Failed \n");
  return -1;
 }
 return dev_fp;
}

int main()

{  
 
   
     struct v4l2_capability cap;
     struct v4l2_format codec_fmt;
/* Camera codec initialization */
 if ((cam_c_fp = cam_c_init()) < 0)
  exit_from_app();
/* Codec set */
 /* Get capability */
 int ret = ioctl(cam_c_fp , VIDIOC_QUERYCAP, &cap);
 if (ret < 0) {
  printf("V4L2 : ioctl on VIDIOC_QUERYCAP failled\n");
  exit(1);
 }
 //printf("V4L2 : Name of the interface is %s\n", cap.driver);

/* Check the type - preview(OVERLAY) */
 if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) {
  printf("V4L2 : Can not capture(V4L2_CAP_VIDEO_CAPTURE is false)\n");
  exit(1);
 }

/* Set format */
 codec_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
 codec_fmt.fmt.pix.width = LCD_WIDTH; 
 codec_fmt.fmt.pix.height = LCD_HEIGHT;  
 codec_fmt.fmt.pix.pixelformat=  V4L2_PIX_FMT_YUYV;//V4L2_PIX_FMT_YUV420;(The average captrue equiment can't support. ) 
 ret = ioctl(cam_c_fp , VIDIOC_S_FMT, &codec_fmt);
 if (ret < 0) {
  printf("V4L2 : ioctl on VIDIOC_S_FMT failled\n");
  exit(1);
 }
        int start=1;
        ret = ioctl(cam_c_fp, VIDIOC_STREAMON, &start);
 if (ret < 0) {
  printf("V4L2 : ioctl on VIDIOC_STREAMON failed\n");
  exit(1);
 }

unsigned char yuv_buf[YUV_FRAME_BUFFER_SIZE];
        char YUV_name[100];
        static FILE *YUV_fp;
        sprintf(&YUV_name[0], "Cam%dx%d-%d.yuv", LCD_WIDTH, LCD_HEIGHT, 888);
        YUV_fp = fopen(&YUV_name[0], "wb");
   if (!YUV_fp) {
    perror(&YUV_name[0]);
   }

int i=0;
        int framecount=200;
           for(i=0;i<framecount;i++)
         {
             /* read YUV frame from camera device */
  if (read(cam_c_fp, yuv_buf, YUV_FRAME_BUFFER_SIZE) < 0) 
               {
   perror("read()");
  }
               
              //write down YUV 
             fwrite(yuv_buf, 1, YUV_FRAME_BUFFER_SIZE, YUV_fp);

}///for

ret = ioctl(cam_c_fp, VIDIOC_STREAMOFF, &start);
 if (ret < 0)
         {
  printf("V4L2 : ioctl on VIDIOC_STREAMOFF failed\n");
  exit(1);
  }
      
        fclose(YUV_fp);
       
        exit_from_app();
  
}

captrue(内存映射方式):

代码如下

captrue_mmap.h

#include <stdio.h>    
#include <stdlib.h>    
#include <string.h>    
#include <assert.h>    
#include <getopt.h>             /* getopt_long() */    
#include <fcntl.h>              /* low-level i/o */    
#include <unistd.h>    
#include <errno.h>    
#include <malloc.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>          /* for videodev2.h */    
#include <linux/videodev2.h>    
#define CLEAR(x) memset (&(x), 0, sizeof (x))    
typedef   enum  {   
    IO_METHOD_READ, IO_METHOD_MMAP, IO_METHOD_USERPTR,   
} io_method;   
struct  buffer {   
    void  * start;   
    size_t  length; //buffer's length is different from cap_image_size    
};   
static   char  * dev_name = NULL;   
static  io_method io = IO_METHOD_MMAP; //IO_METHOD_READ;//IO_METHOD_MMAP;    
static   int  fd = -1;   
struct  buffer * buffers = NULL;   
static  unsigned  int  n_buffers = 0;   
static   FILE  * outf = 0;   
static  unsigned  int  cap_image_size = 0; //to keep the real image size!!

static   void  open_device( void );
static   void  init_device( void );
static   void  init_mmap( void ) ;
static   void  start_capturing( void );
static   void  mainloop( void );
static   int  read_frame( void );
static   void  process_image( const   void  * p,  int  len);
static   void  errno_exit( const   char  * s);
static   int  xioctl( int  fd,  int  request,  void  * arg);
static   void  stop_capturing( void );
static   void  uninit_device( void );
static   void  close_device( void );

captrue_mmap.c

#include "captrue_mmap.h" 
static   void  open_device( void ) {   
    struct  stat st;   
    if  (-1 == stat(dev_name, &st)) {   
        fprintf(stderr, "Cannot identify '%s': %d, %s\n" , dev_name, errno,   
                strerror(errno));   
        exit(EXIT_FAILURE);   
    }   
    if  (!S_ISCHR(st.st_mode)) {   
        fprintf(stderr, "%s is no device\n" , dev_name);   
        exit(EXIT_FAILURE);   
    }   
    fd = open(dev_name, O_RDWR /* required */ | O_NONBLOCK, 0);   
    if  (-1 == fd) {   
        fprintf(stderr, "Cannot open '%s': %d, %s\n" , dev_name, errno,   
                strerror(errno));   
        exit(EXIT_FAILURE);   
    }   
}

static   void  init_device( void ) {   
    struct  v4l2_capability cap;   
    struct  v4l2_cropcap cropcap;   
    struct  v4l2_crop crop;   
    struct  v4l2_format fmt;   
    unsigned int  min;   
    if  (-1 == xioctl(fd, VIDIOC_QUERYCAP, &cap)) 
   {   
        if  (EINVAL == errno)
       {   
            fprintf(stderr, "%s is no V4L2 device\n" , dev_name);   
            exit(EXIT_FAILURE);   
        } 
        else 
       {   
            errno_exit("VIDIOC_QUERYCAP" );   
        }   
    }   
    if  (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE))
   {   
        fprintf(stderr, "%s is no video capture device\n" , dev_name);   
        exit(EXIT_FAILURE);   
    }   
     
    if  (!(cap.capabilities & V4L2_CAP_STREAMING)) 
   {   
            fprintf(stderr, "%s does not support streaming i/o\n" , dev_name);   
            exit(EXIT_FAILURE);   
    }   
       
    //not all capture support crop!!!!!!!    
    /* Select video input, video standard and tune here. */    
    printf("-#-#-#-#-#-#-#-#-#-#-#-#-#-\n" );   
    CLEAR (cropcap);   
    cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;   
    if  (0 == xioctl(fd, VIDIOC_CROPCAP, &cropcap)) {   
        crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;   
#ifndef CROP_BY_JACK    
        crop.c = cropcap.defrect; /* reset to default */    
#else    
        crop.c.left = cropcap.defrect.left;   
        crop.c.top = cropcap.defrect.top;   
        crop.c.width = 352;   
        crop.c.height = 288;   
#endif    
        printf("----->has ability to crop!!\n" );   
        printf("cropcap.defrect = (%d, %d, %d, %d)\n" , cropcap.defrect.left,   
                cropcap.defrect.top, cropcap.defrect.width,   
                cropcap.defrect.height);   
        if  (-1 == xioctl(fd, VIDIOC_S_CROP, &crop)) {   
            switch  (errno) {   
            case  EINVAL:   
                /* Cropping not supported. */    
                break ;   
            default :   
                /* Errors ignored. */    
                break ;   
            }   
            printf("-----!!but crop to (%d, %d, %d, %d) Failed!!\n" ,   
                    crop.c.left, crop.c.top, crop.c.width, crop.c.height);   
        } else  {   
            printf("----->sussess crop to (%d, %d, %d, %d)\n" , crop.c.left,   
                    crop.c.top, crop.c.width, crop.c.height);   
        }   
    } else  {   
        /* Errors ignored. */    
        printf("!! has no ability to crop!!\n" );   
    }   
    printf("-#-#-#-#-#-#-#-#-#-#-#-#-#-\n" );   
    printf("/n" );   
    crop finished!    
    //set the format    
    CLEAR (fmt);   
    fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;   
    fmt.fmt.pix.width = 640;   
    fmt.fmt.pix.height = 480;   
    //V4L2_PIX_FMT_YVU420, V4L2_PIX_FMT_YUV420 鈥?Planar formats with 1/2 horizontal and vertical chroma resolution, also known as YUV 4:2:0    
    //V4L2_PIX_FMT_YUYV 鈥?Packed format with 1/2 horizontal chroma resolution, also known as YUV 4:2:2    
    fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;//V4L2_PIX_FMT_YUV420;//V4L2_PIX_FMT_YUYV;    
    fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;   
    {   
        printf("-#-#-#-#-#-#-#-#-#-#-#-#-#-\n" );   
        printf("=====will set fmt to (%d, %d)--" , fmt.fmt.pix.width,   
                fmt.fmt.pix.height);   
        if  (fmt.fmt.pix.pixelformat == V4L2_PIX_FMT_YUYV) {   
            printf("V4L2_PIX_FMT_YUYV\n" );   
        } else   if  (fmt.fmt.pix.pixelformat == V4L2_PIX_FMT_YUV420) {   
            printf("V4L2_PIX_FMT_YUV420\n" );   
        } else   if  (fmt.fmt.pix.pixelformat == V4L2_PIX_FMT_NV12) {   
            printf("V4L2_PIX_FMT_NV12\n" );   
        }   
    }   
    if  (-1 == xioctl(fd, VIDIOC_S_FMT, &fmt))   
        errno_exit("VIDIOC_S_FMT" );   
    {   
        printf("=====after set fmt\n" );   
        printf("    fmt.fmt.pix.width = %d\n" , fmt.fmt.pix.width);   
        printf("    fmt.fmt.pix.height = %d\n" , fmt.fmt.pix.height);   
        printf("    fmt.fmt.pix.sizeimage = %d\n" , fmt.fmt.pix.sizeimage);   
        cap_image_size = fmt.fmt.pix.sizeimage;   
        printf("    fmt.fmt.pix.bytesperline = %d\n" , fmt.fmt.pix.bytesperline);   
        printf("-#-#-#-#-#-#-#-#-#-#-#-#-#-\n" );   
        printf("/n" );   
    }   
    cap_image_size = fmt.fmt.pix.sizeimage;   
    /* Note VIDIOC_S_FMT may change width and height. */    
    printf("-#-#-#-#-#-#-#-#-#-#-#-#-#-\n" );   
    /* Buggy driver paranoia. */    
    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;   
    printf("After Buggy driver paranoia\n" );   
    printf("    >>fmt.fmt.pix.sizeimage = %d\n" , fmt.fmt.pix.sizeimage);   
    printf("    >>fmt.fmt.pix.bytesperline = %d\n" , fmt.fmt.pix.bytesperline);   
    printf("-#-#-#-#-#-#-#-#-#-#-#-#-#-\n" );   
    printf("\n" );   
     
    
    init_mmap();   
       
}

static   void  init_mmap( void ) 
{   
    struct  v4l2_requestbuffers req;   
    CLEAR (req);   
    req.count = 4;   
    req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;   
    req.memory = V4L2_MEMORY_MMAP;   
    if  (-1 == xioctl(fd, VIDIOC_REQBUFS, &req)) {   
        if  (EINVAL == errno) {   
            fprintf(stderr, "%s does not support "    
                "memory mapping\n" , dev_name);   
            exit(EXIT_FAILURE);   
        } else  {   
            errno_exit("VIDIOC_REQBUFS" );   
        }   
    }   
    if  (req.count < 2) {   
        fprintf(stderr, "Insufficient buffer memory on %s\n" , dev_name);   
        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 == xioctl(fd, VIDIOC_QUERYBUF, &buf))   
            errno_exit("VIDIOC_QUERYBUF" );   
        buffers[n_buffers].length = buf.length;   
        buffers[n_buffers].start = mmap(NULL /* start anywhere */ , buf.length,   
                PROT_READ | PROT_WRITE /* required */ ,   
                MAP_SHARED /* recommended */ , fd, buf.m.offset);   
        if  (MAP_FAILED == buffers[n_buffers].start)   
            errno_exit("mmap" );   
    }   
}
static   void  start_capturing( void ) {   
    unsigned int  i;   
    enum  v4l2_buf_type type;   
    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(fd, VIDIOC_QBUF, &buf))   
                errno_exit("VIDIOC_QBUF" );   
        }   
     type = V4L2_BUF_TYPE_VIDEO_CAPTURE;   
     if  (-1 == xioctl(fd, VIDIOC_STREAMON, &type))   
            errno_exit("VIDIOC_STREAMON" );   
       
}

static   void  mainloop( void )
 {   
    unsigned int  count;   
    count = 1000;   
    while  (count-- > 0) 
    {   
        for  (;;)
        {   
           
            if  (read_frame())   
                break ;   
            /* EAGAIN - continue select loop. */    
        }   
    }   
}

static   int  read_frame( void ) {   
    struct  v4l2_buffer buf;   
    unsigned int  i;   
   
    
        CLEAR (buf);   
        buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;   
        buf.memory = V4L2_MEMORY_MMAP;   
        if  (-1 == xioctl(fd, VIDIOC_DQBUF, &buf)) {   
            switch  (errno) {   
            case  EAGAIN:   
                return  0;   
            case  EIO:   
                /* Could ignore EIO, see spec. */    
                /* fall through */    
            default :   
                errno_exit("VIDIOC_DQBUF" );   
            }   
        }   
        assert(buf.index < n_buffers);   
        //      printf("length = %d/r", buffers[buf.index].length);    
        //      process_image(buffers[buf.index].start, buffers[buf.index].length);    
        printf("image_size = %d,\t IO_METHOD_MMAP buffer.length=%d\r" ,   
                cap_image_size, buffers[0].length);   
        process_image(buffers[0].start, cap_image_size);   
        if  (-1 == xioctl(fd, VIDIOC_QBUF, &buf))   
            errno_exit("VIDIOC_QBUF" );   
      
       
    
    return  1;   
}   
static   void  process_image( const   void  * p,  int  len) {   
    //  static char[115200] Outbuff ;    
    fputc('.' , stdout);   
    if  (len > 0) {   
        fputc('.' , stdout);   
        fwrite(p, 1, len, outf);   
    }   
    fflush(stdout);   

static   void  errno_exit( const   char  * s) {   
    fprintf(stderr, "%s error %d, %s\n" , s, errno, strerror(errno));   
    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;   
}   
static   void  stop_capturing( void ) {   
    enum  v4l2_buf_type type;   
     
        type = V4L2_BUF_TYPE_VIDEO_CAPTURE;   
        if  (-1 == xioctl(fd, VIDIOC_STREAMOFF, &type))   
            errno_exit("VIDIOC_STREAMOFF" );   
    
}   
static   void  uninit_device( void ) {   
    unsigned int  i;   
   
        for  (i = 0; i < n_buffers; ++i)   
            if  (-1 == munmap(buffers[i].start, buffers[i].length))   
                errno_exit("munmap" );   
       
    free(buffers);   
}   
static   void  close_device( void ) {   
    if  (-1 == close(fd))   
        errno_exit("close" );   
    fd = -1;   
}   
int main()
{
    dev_name = "/dev/video0" ;   
    outf = fopen("out.yuv" ,  "wb" );   
    open_device();   
    init_device();   
    start_capturing();   
    mainloop();   
    printf("\n" );   
    stop_capturing();   
    fclose(outf);   
    uninit_device();   
    close_device();   
    exit(EXIT_SUCCESS);   
    return  0;

}

Linux基于v4l2的视频采集(可用)相关推荐

  1. 基于V4L2的视频驱动开发(2) 华清远见 刘洪涛

    基于V4L2的视频驱动开发(2) 华清远见 刘洪涛 三.            V4L2 API及数据结构 V4L2是V4L的升级版本,为linux下视频设备程序提供了一套接口规范.包括一套数据结构和 ...

  2. 基于V4L2的视频驱动开发

    基于V4L2的视频驱动开发 编写基于V4L2视频驱动主要涉及到以下几个知识点: ●摄像头方面的知识 要了解选用的摄像头的特性,包括访问控制方法.各种参数的配置方法.信号输出类型等. ●Camera解码 ...

  3. 基于DSP的视频采集系统设计

    基于DSP的视频采集系统设计 [日期:2008-5-30] 来源:微计算机信息  作者:张杰 北京化工大学 [字体:大 中 小] <script src="http://www.21i ...

  4. 基于V4L2的视频驱动开发(1)

    编写基于V4L2视频驱动主要涉及到以下几个知识点: l         摄像头方面的知识 要了解选用的摄像头的特性,包括访问控制方法.各种参数的配置方法.信号输出类型等. l         Came ...

  5. 基于V4L2的视频驱动开发(1)---Camera

    编写基于V4L2视频驱动主要涉及到以下几个知识点: ●    摄像头方面的知识                 要了解选用的摄像头的特性,包括访问控制方法.各种参数的配置方法.信号输出类型等. ●   ...

  6. V4L2编程 视频采集

    V4L2编程 以前做的智能家居的项目用的是Linux2.6.13的核,使用的中星微的摄像头,移植了spcaview进行图像的获取,后来用了2.6.29的核,发现以前移植的spcaview不能用了,后来 ...

  7. 基于V4L2的视频驱动开发(2

    三.            V4L2 API及数据结构 V4L2是V4L的升级版本,为linux下视频设备程序提供了一套接口规范.包括一套数据结构和底层V4L2驱动接口. 1.常用的结构体在内核目录i ...

  8. linux调用v4l2获取视频,嵌入式Linux:V4L2视频采集操作流程和接口说明

    一般操作流程(视频设备): 1. 打开设备文件. int fd=open("/dev/video0",O_RDWR); 2. 取得设备的capability,看看设备具有什么功能, ...

  9. android 基于ffmpeg将视频采集的nv21转h264格式数据

    准备工作 android studio 2.2以上 本项目是基于ndk 开发的项目,使用android studio + cmake进行开发 cmake配置详情可参考: http://www.jian ...

最新文章

  1. 专访 CNCF 大使张磊:让云原生不再是大厂专属
  2. java判断字符串的值是否为0或者为空
  3. js大屏导出图片_整理了30个实用可视化大屏模板,附源文件+工具
  4. SAP Fiori里Contact Support的按钮渲染逻辑
  5. Windows server 2016 安装小度WiFi网卡驱动
  6. 解决cmd命令行乱码问题
  7. 移动端日期插件rolldate
  8. Java 后台验证码汉字拼音校验
  9. 怎么在微云服务器找一个文件夹,用户怎样了解微云文件在哪里打开
  10. outlook设置京东邮箱
  11. 天牛须matlab,BAS天牛须搜索优化算法.pdf
  12. 二叉树:二叉树的最近公共祖先
  13. If this is an unexpected issue and persists you can inspect it running `pod repo update --verbose`
  14. Python实现中文转拼音功能
  15. PCDATA和CDATA的区别究竟是什么呢?
  16. ACPI中各种state的关系
  17. oracle如何增加initial,Oracle修改表和索引的INITIAL初始化大小
  18. 【2020东京奥运会】奥运榜单以及各国参赛运动员数据可视化~
  19. 可伸缩服务架构:框架与中间件
  20. 投影幕布选购避坑指南!

热门文章

  1. 一个简单的互斥量与条件变量例子
  2. Java革新大提速 Chrome 54终结YouTube的Flash内嵌技术
  3. 石家庄计算机学院生活费每月多少,石家庄高校每月生活费约1000元
  4. java 排序算法总结,Java排序算法总结之归并排序
  5. 服务器放在机柜_服务器网络机柜的保养维护
  6. django ajax页面加载,Python Django 之 基于JQUERY的AJAX 登录页面
  7. kodi教程 linux,Kodi 设置教程 | Homepage of Zhikun Zhang
  8. python生成10个随机数字符串_python生成随机数、随机字符串
  9. PHP计算计算时间差,php中计算时间差的几种方法
  10. .net java xml_java.net.MalformedURLException – 在通过StAX解析XML文件时