zoukankan      html  css  js  c++  java
  • 基于v4l2的webcam应用, 本地预监

    

    本文转自:http://blog.csdn.net/sunkwei/article/details/6530343

    /usr/local/include/libavutil/common.h


    转载内容

    今天尝试编写了一个基于 v4l2 的摄像头应用, 目前仅仅实现从摄像头捕捉视频, 然后本地回显.

    照例先上效果图, 其中左侧小点为预监窗口, 右侧为经过 x264 压缩, tcp 传输, libavcodec 解压, 再用 qt 显示的效果., 延迟很低很低 :)

    主要就是以下几个知识点: 

        1. v4l2接口:

        2. X11的本地回显:

        3. 使用 libswscale 进行拉伸:

        4. 使用 libx264 压缩:

    1. v4l2接口: 大眼一看, 密密丫丫的 VIDIOC_XXXX, 其实静下心来, 也没多少, 很清晰, 大体流程如下:

                 capture_open(name)

                          open /dev/video0         // 打开设备

                          check driver caps          // 检查一些 caps

                          VIDIOC_REQBUFS         // 使用 streaming mode,  mmap mode, 分配

                              VIDIOC_QUERYBUF       // 获取分配的buf, 并且mmap到进程空间

                              mmap

                           VIDIOC_QBUF              // buf 入列

                           VIDIOC_STREAMON      // 开始

    使用的数据结构

    1. struct Buffer  
    2. {  
    3.     void *start;  
    4.     size_t length;  
    5. };  
    6. typedef struct Buffer Buffer;  
    7. struct Ctx  
    8. {  
    9.     int vid;  
    10.     int width, height;  // 输出图像大小  
    11.     struct SwsContext *sws; // 用于转换  
    12.     int rows;   // 用于 sws_scale()  
    13.     int bytesperrow; // 用于cp到 pic_src  
    14.     AVPicture pic_src, pic_target;  // 用于 sws_scale  
    15.     Buffer bufs[2];     // 用于 mmap  
    16. };  
    17. typedef struct Ctx Ctx;  

     

    capture_open(...) 打开设备

    1. void *capture_open (const char *dev_name, int t_width, int t_height)  
    2. {  
    3.     int id = open(dev_name, O_RDWR);  
    4.     if (id < 0) return 0;  
    5.     Ctx *ctx = new Ctx;  
    6.     ctx->vid = id;  
    7.     // to query caps  
    8.     v4l2_capability caps;  
    9.     ioctl(id, VIDIOC_QUERYCAP, &caps);  
    10.     if (caps.capabilities & V4L2_CAP_VIDEO_CAPTURE) {  
    11.         if (caps.capabilities & V4L2_CAP_READWRITE) {  
    12.             // TODO: ...  
    13.         }  
    14.         if (caps.capabilities & V4L2_CAP_STREAMING) {  
    15.             // 检查是否支持 MMAP, 还是 USERPTR  
    16.             v4l2_requestbuffers bufs;  
    17.             memset(&bufs, 0, sizeof(bufs));  
    18.             bufs.count = 2;  
    19.             bufs.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;  
    20.             bufs.memory = V4L2_MEMORY_MMAP;  
    21.             if (ioctl(id, VIDIOC_REQBUFS, &bufs) < 0) {  
    22.                 fprintf(stderr, "%s: don't support MEMORY_MMAP mode!/n", __func__);  
    23.                 close(id);  
    24.                 delete ctx;  
    25.                 return 0;  
    26.             }  
    27.             fprintf(stderr, "%s: using MEMORY_MMAP mode, buf cnt=%d/n", __func__, bufs.count);  
    28.             // mmap  
    29.             for (int i = 0; i < 2; i++) {  
    30.                 v4l2_buffer buf;  
    31.                 memset(&buf, 0, sizeof(buf));  
    32.                 buf.type = bufs.type;  
    33.                 buf.memory = bufs.memory;  
    34.                 if (ioctl(id, VIDIOC_QUERYBUF, &buf) < 0) {  
    35.                     fprintf(stderr, "%s: VIDIOC_QUERYBUF ERR/n", __func__);  
    36.                     close(id);  
    37.                     delete ctx;  
    38.                     return 0;  
    39.                 }  
    40.                 ctx->bufs[i].length = buf.length;  
    41.                 ctx->bufs[i].start = mmap(0, buf.length, PROT_READ|PROT_WRITE,  
    42.                         MAP_SHARED, id, buf.m.offset);  
    43.             }  
    44.         }  
    45.         else {  
    46.             fprintf(stderr, "%s: can't support read()/write() mode and streaming mode/n", __func__);  
    47.             close(id);  
    48.             delete ctx;  
    49.             return 0;  
    50.         }  
    51.     }  
    52.     else {  
    53.         fprintf(stderr, "%s: can't support video capture!/n", __func__);  
    54.         close(id);  
    55.         delete ctx;  
    56.         return 0;  
    57.     }  
    58.     int rc;  
    59.     // enum all support image fmt  
    60.     v4l2_fmtdesc fmt_desc;  
    61.     uint32_t index = 0;  
    62.     // 看起来, 不支持 plane fmt, 直接使用 yuyv 吧, 然后使用 libswscale 转换  
    63. #if 0     
    64.     do {  
    65.         fmt_desc.index = index;  
    66.         fmt_desc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;  
    67.         rc = ioctl(id, VIDIOC_ENUM_FMT, &fmt_desc);  
    68.         if (rc >= 0) {  
    69.             fprintf(stderr, "/t support %s/n", fmt_desc.description);  
    70.         }  
    71.         index++;  
    72.     } while (rc >= 0);  
    73. #endif // 0  
    74.     v4l2_format fmt;  
    75.     fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;  
    76.     rc = ioctl(id, VIDIOC_G_FMT, &fmt);  
    77.     if (rc < 0) {  
    78.         fprintf(stderr, "%s: can't VIDIOC_G_FMT.../n", __func__);  
    79.         return 0;  
    80.     }  
    81.     PixelFormat pixfmt = PIX_FMT_NONE;  
    82.     switch (fmt.fmt.pix.pixelformat) {  
    83.     case V4L2_PIX_FMT_YUYV:  
    84.         pixfmt = PIX_FMT_YUYV422;  
    85.         break;  
    86.     }  
    87.     if (pixfmt == PIX_FMT_NONE) {  
    88.         fprintf(stderr, "%s: can't support %4s/n", __func__, (char*)&fmt.fmt.pix.pixelformat);  
    89.         return 0;  
    90.     }  
    91.     // 构造转换器  
    92.     fprintf(stderr, "capture_width=%d, height=%d, stride=%d/n", fmt.fmt.pix.width, fmt.fmt.pix.height,  
    93.             fmt.fmt.pix.bytesperline);  
    94.     ctx->width = t_width;  
    95.     ctx->height = t_height;  
    96.     ctx->sws = sws_getContext(fmt.fmt.pix.width, fmt.fmt.pix.height, pixfmt,  
    97.             ctx->width, ctx->height, PIX_FMT_YUV420P,     // PIX_FMT_YUV420P 对应 X264_CSP_I420  
    98.             SWS_FAST_BILINEAR, 0, 0, 0);  
    99.     ctx->rows = fmt.fmt.pix.height;  
    100.     ctx->bytesperrow = fmt.fmt.pix.bytesperline;  
    101.     avpicture_alloc(&ctx->pic_target, PIX_FMT_YUV420P, ctx->width, ctx->height);  
    102.     // queue buf  
    103.     for (int i = 0; i < sizeof(ctx->bufs)/sizeof(Buffer); i++) {  
    104.         v4l2_buffer buf;  
    105.         memset(&buf, 0, sizeof(buf));  
    106.         buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;  
    107.         buf.memory = V4L2_MEMORY_MMAP;  
    108.         buf.index = i;  
    109.         if (ioctl(id, VIDIOC_QBUF, &buf) < 0) {  
    110.             fprintf(stderr, "%s: VIDIOC_QBUF err/n", __func__);  
    111.             exit(-1);  
    112.         }  
    113.     }  
    114.     int type = V4L2_BUF_TYPE_VIDEO_CAPTURE;  
    115.     if (ioctl(id, VIDIOC_STREAMON, &type) < 0) {  
    116.         fprintf(stderr, "%s: VIDIOC_STREAMON err/n", __func__);  
    117.         exit(-1);  
    118.     }  
    119.     return ctx;  
    120. }  

     

                  capture_get_pic()

                           VIDIOC_DQBUF            // 出列, 

                           sws_scale                    // 格式转换/拉伸到 PIX_FMT_YUV420P, 准备方便压缩

                           VIDIOC_QBUF              // 重新入列

    capture_get_picture(...) 从摄像头得到一帧图片

    1. int capture_get_picture (void *id, Picture *pic)  
    2. {  
    3.     // 获取, 转换  
    4.     Ctx *ctx = (Ctx*)id;  
    5.     v4l2_buffer buf;  
    6.     memset(&buf, 0, sizeof(buf));  
    7.     buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;  
    8.     buf.memory = V4L2_MEMORY_MMAP;  
    9.     if (ioctl(ctx->vid, VIDIOC_DQBUF, &buf) < 0) {  
    10.         fprintf(stderr, "%s: VIDIOC_DQBUF err/n", __func__);  
    11.         return -1;  
    12.     }  
    13. //  _save_pic(ctx->bufs[buf.index].start, buf.length);  
    14. //  __asm("int $3");  
    15.     ctx->pic_src.data[0] = (unsigned char*)ctx->bufs[buf.index].start;  
    16.     ctx->pic_src.data[1] = ctx->pic_src.data[2] = ctx->pic_src.data[3] = 0;  
    17.     ctx->pic_src.linesize[0] = ctx->bytesperrow;  
    18.     ctx->pic_src.linesize[1] = ctx->pic_src.linesize[2] = ctx->pic_src.linesize[3] = 0;  
    19.     // sws_scale  
    20.     int rs = sws_scale(ctx->sws, ctx->pic_src.data, ctx->pic_src.linesize,  
    21.             0, ctx->rows, ctx->pic_target.data, ctx->pic_target.linesize);  
    22.     // out  
    23.     for (int i = 0; i < 4; i++) {  
    24.         pic->data[i] = ctx->pic_target.data[i];  
    25.         pic->stride[i] = ctx->pic_target.linesize[i];  
    26.     }  
    27.     // re queue buf  
    28.     if (ioctl(ctx->vid, VIDIOC_QBUF, &buf) < 0) {  
    29.         fprintf(stderr, "%s: VIDIOC_QBUF err/n", __func__);  
    30.         return -1;  
    31.     }  
    32.     return 1;  
    33. }  

     

    2.  X11 的本地回显: 采用 XShm, 效率还行

                  vs_open ()

                            XOpenDisplay()

                            XCreateSimpleWindow()

                            XCreateGC()

                            XMapWindow()

                            XShmCreateImage()

                            shmget()

                            shmat()

    使用的数据结构

    1. struct Ctx  
    2. {  
    3.     Display *display;  
    4.     int screen;  
    5.     Window window;  
    6.     GC gc;  
    7.     XVisualInfo vinfo;  
    8.     XImage *image;  
    9.     XShmSegmentInfo segment;  
    10.     SwsContext *sws;  
    11.     PixelFormat target_pixfmt;  
    12.     AVPicture pic_target;  
    13.     int v_width, v_height;  
    14.     int curr_width, curr_height;  
    15. };  
    16. typedef struct Ctx Ctx;  

     

    vs_open(...) 打开设备

    1. void *vs_open (int v_width, int v_height)  
    2. {  
    3.     Ctx *ctx = new Ctx;  
    4.     ctx->v_width = v_width;  
    5.     ctx->v_height = v_height;  
    6.     // window  
    7.     ctx->display = XOpenDisplay(0);  
    8.     ctx->window = XCreateSimpleWindow(ctx->display, RootWindow(ctx->display, 0),  
    9.             100, 100, v_width, v_height, 0, BlackPixel(ctx->display, 0),  
    10.             WhitePixel(ctx->display, 0));  
    11.     ctx->screen = 0;  
    12.     ctx->gc = XCreateGC(ctx->display, ctx->window, 0, 0);  
    13.       
    14.     XMapWindow(ctx->display, ctx->window);  
    15.     // current screen pix fmt  
    16.     Window root;  
    17.     unsigned int cx, cy, border, depth;  
    18.     int x, y;  
    19.     XGetGeometry(ctx->display, ctx->window, &root, &x, &y, &cx, &cy, &border, &depth);  
    20.     // visual info  
    21.     XMatchVisualInfo(ctx->display, ctx->screen, depth, DirectColor, &ctx->vinfo);  
    22.     // image  
    23.     ctx->image = XShmCreateImage(ctx->display, ctx->vinfo.visual, depth, ZPixmap, 0,  
    24.             &ctx->segment, cx, cy);  
    25.     if (!ctx->image) {  
    26.         fprintf(stderr, "%s: can't XShmCreateImage !/n", __func__);  
    27.         exit(-1);  
    28.     }  
    29.     ctx->segment.shmid = shmget(IPC_PRIVATE,  
    30.             ctx->image->bytes_per_line * ctx->image->height,   
    31.             IPC_CREAT | 0777);  
    32.     if (ctx->segment.shmid < 0) {  
    33.         fprintf(stderr, "%s: shmget err/n", __func__);  
    34.         exit(-1);  
    35.     }  
    36.     ctx->segment.shmaddr = (char*)shmat(ctx->segment.shmid, 0, 0);  
    37.     if (ctx->segment.shmaddr == (char*)-1) {  
    38.         fprintf(stderr, "%s: shmat err/n", __func__);  
    39.         exit(-1);  
    40.     }  
    41.     ctx->image->data = ctx->segment.shmaddr;  
    42.     ctx->segment.readOnly = 0;  
    43.     XShmAttach(ctx->display, &ctx->segment);  
    44.     PixelFormat target_pix_fmt = PIX_FMT_NONE;  
    45.     switch (ctx->image->bits_per_pixel) {  
    46.         case 32:  
    47.             target_pix_fmt = PIX_FMT_RGB32;  
    48.             break;  
    49.         case 24:  
    50.             target_pix_fmt = PIX_FMT_RGB24;  
    51.             break;  
    52.         default:  
    53.             break;  
    54.     }  
    55.     if (target_pix_fmt == PIX_FMT_NONE) {  
    56.         fprintf(stderr, "%s: screen depth format err/n", __func__);  
    57.         delete ctx;  
    58.         return 0;  
    59.     }  
    60.     // sws  
    61.     ctx->target_pixfmt = target_pix_fmt;  
    62.     ctx->curr_width = cx;  
    63.     ctx->curr_height = cy;  
    64.     ctx->sws = sws_getContext(v_width, v_height, PIX_FMT_YUV420P,  
    65.             cx, cy, target_pix_fmt,  
    66.             SWS_FAST_BILINEAR, 0, 0, 0);  
    67.     avpicture_alloc(&ctx->pic_target, target_pix_fmt, cx, cy);  
    68.     XFlush(ctx->display);  
    69.     return ctx;  
    70. }  

     

                   vs_show()

                            sws_scale()                // 拉伸到当前窗口大小, 转换格式

                            XShmPutImage()        // 显示, 呵呵, 真的很简单

    vs_show(...) 主要代码都是处理窗口变化的

    1. int vs_show (void *ctx, unsigned char *data[4], int stride[4])  
    2. {  
    3.     // 首选检查 sws 是否有效, 根据当前窗口大小决定  
    4.     Ctx *c = (Ctx*)ctx;  
    5.     Window root;  
    6.     int x, y;  
    7.     unsigned int cx, cy, border, depth;  
    8.     XGetGeometry(c->display, c->window, &root, &x, &y, &cx, &cy, &border, &depth);  
    9.     if (cx != c->curr_width || cy != c->curr_height) {  
    10.         avpicture_free(&c->pic_target);  
    11.         sws_freeContext(c->sws);  
    12.         c->sws = sws_getContext(c->v_width, c->v_height, PIX_FMT_YUV420P,  
    13.                 cx, cy, c->target_pixfmt,   
    14.                 SWS_FAST_BILINEAR, 0, 0, 0);  
    15.         avpicture_alloc(&c->pic_target, c->target_pixfmt, cx, cy);  
    16.         c->curr_width = cx;  
    17.         c->curr_height = cy;  
    18.         // re create image  
    19.         XShmDetach(c->display, &c->segment);  
    20.         shmdt(c->segment.shmaddr);  
    21.         shmctl(c->segment.shmid, IPC_RMID, 0);  
    22.         XDestroyImage(c->image);  
    23.         c->image = XShmCreateImage(c->display, c->vinfo.visual, depth, ZPixmap, 0,  
    24.             &c->segment, cx, cy);  
    25.         c->segment.shmid = shmget(IPC_PRIVATE,  
    26.                 c->image->bytes_per_line * c->image->height,  
    27.                 IPC_CREAT | 0777);  
    28.         c->segment.shmaddr = (char*)shmat(c->segment.shmid, 0, 0);  
    29.         c->image->data = c->segment.shmaddr;  
    30.         c->segment.readOnly = 0;  
    31.         XShmAttach(c->display, &c->segment);  
    32.     }  
    33.     //   
    34.     sws_scale(c->sws, data, stride, 0, c->v_height, c->pic_target.data, c->pic_target.linesize);  
    35.     // cp to image  
    36.     unsigned char *p = c->pic_target.data[0], *q = (unsigned char*)c->image->data;  
    37.     int xx = MIN(c->image->bytes_per_line, c->pic_target.linesize[0]);  
    38.     for (int i = 0; i < c->curr_height; i++) {  
    39.         memcpy(q, p, xx);  
    40.         p += c->image->bytes_per_line;  
    41.         q += c->pic_target.linesize[0];  
    42.     }  
    43.     // 显示到 X 上  
    44.     XShmPutImage(c->display, c->window, c->gc, c->image, 0, 0, 0, 0, c->curr_width, c->curr_height, 1);  
    45.     return 1;  
    46. }  

     

    3. libswscale: 用于picture格式/大小转换, 占用cpu挺高 :), 用起来很简单, 基本就是

                    sws = sws_getContext(....);

                    sws_scale(sws, ...)

    4. libx264 压缩: 考虑主要用于互动, 所以使用 preset=fast, tune=zerolatency, 320x240, 10fps, 300kbps, jj实测延迟很低, 小于 100ms

    使用的数据结构

    1. struct Ctx  
    2. {  
    3.     x264_t *x264;  
    4.     x264_picture_t picture;  
    5.     x264_param_t param;  
    6.     void *output;       // 用于保存编码后的完整帧  
    7.     int output_bufsize, output_datasize;  
    8.     int64_t pts;        // 输入 pts  
    9.     int64_t (*get_pts)(struct Ctx *);  
    10.     int64_t info_pts, info_dts;  
    11.     int info_key_frame;  
    12.     int info_valid;  
    13. };  

     

        vc_open(...) 设置必要的参数, 打开编码器

    1. void *vc_open (int width, int height)  
    2. {  
    3.     Ctx *ctx = new Ctx;  
    4.     // 设置编码属性  
    5.     //x264_param_default(&ctx->param);  
    6.     x264_param_default_preset(&ctx->param, "fast""zerolatency");  
    7.     ctx->param.i_width = width;  
    8.     ctx->param.i_height = height;  
    9.     ctx->param.b_repeat_headers = 1;  // 重复SPS/PPS 放到关键帧前面  
    10.     ctx->param.b_cabac = 1;  
    11.     ctx->param.i_fps_num = 10;  
    12.     ctx->param.i_fps_den = 1;  
    13.     ctx->param.i_keyint_max = 30;  
    14.     ctx->param.i_keyint_min = 10;  
    15.     // rc  
    16.     ctx->param.rc.i_rc_method = X264_RC_CRF;  
    17.     ctx->param.rc.i_bitrate = 300;  
    18.     //ctx->param.rc.f_rate_tolerance = 0.1;  
    19.     //ctx->param.rc.i_vbv_max_bitrate = ctx->param.rc.i_bitrate * 1.3;  
    20.     //ctx->param.rc.f_rf_constant = 600;  
    21.     //ctx->param.rc.f_rf_constant_max = ctx->param.rc.f_rf_constant * 1.3;  
    22. #ifdef DEBUG  
    23.     ctx->param.i_log_level = X264_LOG_WARNING;  
    24. #else  
    25.     ctx->param.i_log_level = X264_LOG_NONE;  
    26. #endif // release  
    27.     ctx->x264 = x264_encoder_open(&ctx->param);  
    28.     if (!ctx->x264) {  
    29.         fprintf(stderr, "%s: x264_encoder_open err/n", __func__);  
    30.         delete ctx;  
    31.         return 0;  
    32.     }  
    33.     x264_picture_init(&ctx->picture);  
    34.     ctx->picture.img.i_csp = X264_CSP_I420;  
    35.     ctx->picture.img.i_plane = 3;  
    36.     ctx->output = malloc(128*1024);  
    37.     ctx->output_bufsize = 128*1024;  
    38.     ctx->output_datasize = 0;  
    39.     ctx->get_pts = first_pts;  
    40.     ctx->info_valid = 0;  
    41.     return ctx;  
    42. }  

     

    vc_compress(...) 压缩, 如果成功, 得到串流

    1. static int encode_nals (Ctx *c, x264_nal_t *nals, int nal_cnt)  
    2. {  
    3.     char *pout = (char*)c->output;  
    4.     c->output_datasize = 0;  
    5.     for (int i = 0; i < nal_cnt; i++) {  
    6.         if (c->output_datasize + nals[i].i_payload > c->output_bufsize) {  
    7.             // 扩展  
    8.             c->output_bufsize = (c->output_datasize+nals[i].i_payload+4095)/4096*4096;  
    9.             c->output = realloc(c->output, c->output_bufsize);  
    10.         }  
    11.         memcpy(pout+c->output_datasize, nals[i].p_payload, nals[i].i_payload);  
    12.         c->output_datasize += nals[i].i_payload;  
    13.     }  
    14.     return c->output_datasize;  
    15. }  
    16. int vc_compress (void *ctx, unsigned char *data[4], int stride[4], const void **outint *len)  
    17. {  
    18.     Ctx *c = (Ctx*)ctx;  
    19.       
    20.     // 设置 picture 数据  
    21.     for (int i = 0; i < 4; i++) {  
    22.         c->picture.img.plane[i] = data[i];  
    23.         c->picture.img.i_stride[i] = stride[i];  
    24.     }  
    25.     // encode  
    26.     x264_nal_t *nals;  
    27.     int nal_cnt;  
    28.     x264_picture_t pic_out;  
    29.       
    30.     c->picture.i_pts = c->get_pts(c);  
    31. #ifdef DEBUG_MORE  
    32.     static int64_t _last_pts = c->picture.i_pts;  
    33.     fprintf(stderr, "DBG: pts delta = %lld/n", c->picture.i_pts - _last_pts);  
    34.     _last_pts = c->picture.i_pts;  
    35. #endif //  
    36.     x264_picture_t *pic = &c->picture;  
    37.     do {  
    38.         // 这里努力消耗掉 delayed frames ???  
    39.         // 实际使用 zerolatency preset 时, 效果足够好了  
    40.         int rc = x264_encoder_encode(c->x264, &nals, &nal_cnt, pic, &pic_out);  
    41.         if (rc < 0) return -1;  
    42.         encode_nals(c, nals, nal_cnt);  
    43.     } while (0);  
    44.     *out = c->output;  
    45.     *len = c->output_datasize;  
    46.     if (nal_cnt > 0) {  
    47.         c->info_valid = 1;  
    48.         c->info_key_frame = pic_out.b_keyframe;  
    49.         c->info_pts = pic_out.i_pts;  
    50.         c->info_dts = pic_out.i_dts;  
    51.     }  
    52.     else {  
    53.         fprintf(stderr, ".");  
    54.         return 0; // 继续  
    55.     }  
    56.  
    57. #ifdef DEBUG_MORE  
    58.     static size_t _seq = 0;  
    59.     fprintf(stderr, "#%lu: [%c] frame type=%d, size=%d/n", _seq,   
    60.             pic_out.b_keyframe ? '*' : '.',   
    61.             pic_out.i_type, c->output_datasize);  
    62.     _seq++;  
    63. #endif // debug  
    64.     return 1;  
    65. }  

     

    附上源码: 唉, 源码是不停更新的, csdn居然没有一个类似 git, svn 之类的仓库, 算了, 如果有人要, email吧.

          main.cpp          主流程

          capture.cpp, capture.h    获取 v4l2 的图像帧

          vcompress.cpp vcompress.h 实现 x264 的压缩

          vshow.cpp vsho.h   用 X11 显示实时图像


    解压

    解压之后直接make 如果报错出现UINT64_C未定义,在/usr/local/include/libavutil/common.h   或者是/usr/include/libavutil/common.h文件中添加

    #ifndef INT64_C
    #define INT64_C(c) (c ## LL)
    #define UINT64_C(c) (c ## ULL)
    #endif


  • 相关阅读:
    uitableview中文排序问题
    跳转到指定页面popToViewController用法
    [__NSCFConstantString size]: unrecognized selector sent to instance 错误
    [NSThread sleepForTimeInterval:3.0];
    XCode 4.3 Unable to load persistent store UserDictionary.sqlite 以及 ios simulator failed to install the application
    ios 6.0模拟器页面调出pop窗口消失后无法使用键盘
    iOS真机测试,为Provisioning添加设备
    [IOS笔记]
    UITableView 滚动时使用reloaddata出现 crash'-[__NSCFArray objectAtIndex:]: index (1) beyond bounds (0)' Crash
    C# 自定义用户控件
  • 原文地址:https://www.cnblogs.com/xiabodan/p/4038583.html
Copyright © 2011-2022 走看看