精华内容
下载资源
问答
  • H264是当今流行的视频压缩格式;ffmpeg是一个开源库,实现了对h264视频文件的解压缩。 为了降低使用ffmpeg的复杂性,尽量隐藏实现细节,我写了一个封装库。c#也可以很方便的使用此库。解压后的数据可以为yuv格式,...
  • 基于basler工业相机的H264压缩,Basler工业相机始终是您的理想之选。自1988年以来,Basler一直专注于开发和生产用于工业图像处理的高质量数字相机。如今,我们已发展成为全球领先企业之一。 作为世界领先的高质量...
  • ffmpeg 库及.h 支持H264及H265编解码
  • h264格式视频文件播放

    2018-01-03 20:24:26
    h264视频文件播放demo,原文博客地址:http://blog.csdn.net/u013171283/article/details/78961679
  • H.264 Encoder中文版 可以将其他视频文件编码为 H.264/AVC 编码的视频文件,使用这个小而强大的工具,您可以自己创建一个 H.264 电影,这是一个很好的实用工具。H.264 Encoder 很容易使用,帮助任何初学者在几分钟内...
  • 该文档介绍了一些 H264+ 和 H265+ 视频编码实现原理。文档是海康的技术说明文档,英文版本
  • H264压缩比 条件: 1、YUV格式为YUV420。 乘1.5倍,rgb是3倍 2、分辨率为640x480 3、帧率为15 原视频未压缩需要6404801.5158 = 55296000(bit),约55M (一秒). H264建议码流为500kps 结果: 约1/100 码流参考值 常用的h...

    H264压缩比

    条件:
    1、YUV格式为YUV420。 乘1.5倍,rgb是3倍
    2、分辨率为640x480
    3、帧率为15
    原视频未压缩需要6404801.5158 = 55296000(bit),约55M (一秒).
    H264建议码流为500kps
    结果: 约1/100

    码流参考值

    常用的h264建议码流都并非科学计算得出的,而是各个厂家经过大量实践得出的经验值。
    常用的查询网址:

    https://docs.agora.io/cn

    这是声网总结的经验值,声网在音视频领域是比较权威的。
    1、进入网页
    2、点击视频通话
    3、在基础功能里找到设置视频编码属性并点击
    4、下翻找到数据

    • 二人视频通话场景:
      分辨率 320 × 240、帧率 15 fps、码率 200 Kbps
      分辨率 640 × 360、帧率 15 fps、码率 400 Kbps
    • 多人视频通话场景:
      分辨率 160 × 120、帧率 15 fps、码率 65 Kbps
      分辨率 320 × 180、帧率 15 fps、码率 140 Kbps
      分辨率 320 × 240、帧率 15 fps、码率 200 Kbps

    视频属性参考表

    分辨率 (宽 × 高)帧率 (fps)基准码率 (Kbps,适用于通信)直播码率 (Kbps,适用于直播)
    160 × 1201565130
    120 × 1201550100
    320 × 18015140280
    180 × 18015100200
    240 × 18015120240
    320 × 24015200400
    240 × 24015140280
    424 × 24015220440
    640 × 36015400800
    360 × 36015260520
    640 × 360306001200
    360 × 36030400800
    480 × 36015320640
    480 × 36030490980
    640 × 480155001000
    480 × 48015400800
    640 × 480307501500
    480 × 480306001200
    848 × 480156101220
    848 × 480309301860
    640 × 48010400800
    1280 × 7201511302260
    1280 × 7203017103420
    960 × 720159101820
    960 × 7203013802760

    GOP在这里插入图片描述

    假设一秒钟有25帧,如上图所示。可以算出来帧与帧之间间隔40毫秒。
    将时间拉长至10分钟时,累计的帧数就已经是十分多了,需要处理的数据增加,带来了压缩时的困难。
    在这里插入图片描述
    为了处理,则将这些帧进行了分组。
    在这里插入图片描述
    假设小人dou时看望远镜的,包括看的角度不同的,分为一组,小人所有敲键盘的分为另一组。
    每一帧的敲键盘都是一个动作(按下去、抬起来、抬得不同高度等)。
    所以每一个gop都是描述的目标的细微差别。所以在每一个GOP中的所有视频帧都是强相关的,而不同GOP间的视频帧相关性特别小。
    所以由此可以将视频划分为很多不同的组,每一组都是强相关的视频帧,GOP即Grouo Of Picture.

    GOP中帧与帧之间的差别小

    在这里插入图片描述
    通过GOP的划分,组内的压缩就容易进行了,因为差别小,数据量会很容易的下降下去。
    以小人为例,每张图片的背景几乎没有差别,只是小人的望远镜方向不同,姿势不同,所以背景完全可以放在一张图里。
    小人还可以进行再次划分,例如小人的头发都是相同的,也可以放在一张图里。
    所以这三张(实际更多)的图片公共点非常多,背景完全一样,头发完全一样,所以只要记录每张图中的不同点,也就是望远镜角度,身体姿势等。

    展开全文
  • h264转码MP4 支持html5播放格式 示例代码: H264TrackImpl h264TrackImpl = new H264TrackImpl(new FileDataSourceImpl("C://a.264")); Movie movie = new Movie(); movie.addTrack(h264TrackImpl); Container ...
  • C#H264格式视频解码

    2019-01-09 11:37:44
    C#H264格式视频解码, 使用FFMPEG解码为H264视频文件,解码后的数据保存为.YUV,可以使用VLC等播放器播放。
  • 今天小编就为大家分享一篇python opencv图片编码为h264文件的实例,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
  • H264格式详解

    千次阅读 2021-03-09 15:28:08
    H264是属于视频的编码层的标准格式,视频编码显然是为了压缩大小。 我们看下一个完全没压缩的视频数据大小。假设视频是高清(1280 * 720),每秒30帧,也就是每秒的数据 1280 * 720 *30 / 8(字节) /1024(KB)/...

    H264基础简介

    前言

    H264是属于视频的编码层的标准格式,视频编码显然是为了压缩大小。
    我们看下一个完全没压缩的视频数据大小。假设视频是高清(1280 * 720),每秒30帧,也就是每秒的数据

    1280 * 720 *30 / 8(字节) /1024(KB)/1024(MB) = 3.11MB

    那么90分钟的电影就要16.7GB,这个数据量显然在当前网络下是不现实的。

    视频压缩的原理就是去除视频冗余部分,下面列举下
    1,时间冗余
    时间冗余是序列图像(电视图像、动画)和语音数据中所经常包含的冗余。
    图像序列中的两幅相邻的图像,后一幅图像与前一幅图像之间有较大的相关性,这反映为时间冗余。同理,在语言中,由于人在说话时发音的音频是一连续的渐变过程,而不是一个完全的在时间上独立的过程,因而存在时间冗余。
    2,空间冗余
    空间冗余是图像数据中经常存在的一种冗余。
    在同一幅图像中,规则物体和规则背景(所谓规则是指表面颜色分布是有序的而不是杂乱无章的)的表面物理特性具有相关性,这些相关性的光成像结构在数字化图像中就表现为数据冗余。,
    3,知识冗余
    有许多图像的理解与某些基础知识有相当大的相关性,
    例如:人脸的图像有固定的结构。比如,嘴的上方有鼻子。鼻子的上方有眼睛,鼻子位于正脸图像的中线上等等。这类规律性的结构可由先验知识相背景知识得到,我们称此类冗余为知识冗余。
    4,结构冗余
    有些图像从大域上看存在着非常强的纹理结构,例如布纹图像和草席图像,我们说它们在结构上存在冗余。
    5,视觉冗余
    人类视觉系统对于图像场的任何变化,并不是都能感知的。例如,对于图像的编码和解码处理时,由于压缩或量比截断引入了噪声而使图像发生了一些变化,如果这些变化不能为视觉所感知,则仍认为图像足够好。事实上人类视觉系统一般的分辨能力约为26灰度等级,而一般图像量化采用28灰度等级,这类冗余我们称为视觉冗余。
    通常情况下,人类视觉系统对亮度变化敏感,而对色度的变化相对不敏感;在高亮度区,人眼对亮度变化敏感度下降。
    对物体边缘敏感,内部区域相对不敏感;对整体结构敏感,而对内部细节相对不敏感。
    6,信息熵冗余
    信息熵是指一组数据所携带的信息量。它一般定义为:H =
    -∑pi×log2pi。其中N为码元个数,pi为码元yi发生的概率。由定义,为使单位数据量d接近于或等于H,应设d=∑pi×b(yi),其中b(yi)是分配给码元yi的比特数,理论上应取-log2pi。实际上在应用中很难估计出{Po,P1,…,PN—1}。因此一般取b(yo)=b(y1)=…=b(yN—1),例如,英文字母编码码元长为7比特,即b(yo)=b(y1)=…=b(yN—1)=7,这样所得的d必然大于H,由此带来的冗余称为信息墒冗余或编码冗余。

    H264原始码流结构

    组成:
    H264功能分为两层,VCL(视频编码层)和 NAL(网络提取层).

    1. VCL:包括核心压缩引擎和块,宏块和片的语法级别定义,设计目标是尽可能地独立于网络进行高效的编码。
    2. NAL:负责将VCL产生的比特字符串适配到各种各样的网络和多元环境中,覆盖了所有片级以上的语法级别。

     

     

    VCL数据传输或者存储之前,会被映射到一个NALU中,H264数据包含一个个NALU。如下图

     

     


    一个NALU = 一组对应于视频编码的NALU头部信息 + 一个原始字节序列负荷(RBSP,Raw Byte Sequence Payload).

     

     

     

    一个原始的NALU单元结构如下
    [StartCode][NALU Header][NALU Payload]三部分。

     

    1. NAL Header

     

     

     

     


    头信息协议如上图。

    例如: 1 00 00 00 01 06: SEI信息
    2 00 00 00 01 67: 0x67&0x1f = 0x07 :SPS
    3 00 00 00 01 68: 0x68&0x1f = 0x08 :PPS
    4 00 00 00 01 65: 0x65&0x1f = 0x05: IDR Slice


     

    1. RBSP

     

     

     

    下面是RBSP序列的描述

     

     

    H264的码流分层结构

     

     

     

    下面我们挨个来看每层的结构.

    1. Slice(片)

    可以看到NALU的主体是slice。
    slice是H264提出的新概念,编码图片后切分高效整合而成。
    一个图片有一个或者多个slice。通过NALU装载网络传输。

    设置片的目的是为了限制误码的扩散和传输,编码片是项目独立的,一个片的预测不能以其他片中的宏块为参考图像。保证了某一片的预测误差不会传播到别的片。

    一个slice同样包含Slice Header + Slice Data

    slice有以下五种类型

    (1) I -slice: slice的全部MB(宏块)都采用intra-prediction(帧内预测)的方式来编码;
    (2) P-slice: slice中的MB(宏块)使用intra-prediction(帧内预测)和inter-prediction(帧间预测)的方式来编码,但每一个inter-prediction block最多只能使用一个移动向量;
    (3) B-slice:与P-slice类似,但每一个inter-prediction block可以使用二个移动向量。B-slice的‘B’是指Bi-predictive(双向预测),除了可由前一张和后一张影像的I(或P、B)-slice外,也能从前二张不同影像的I(或P、B)-slice来做inter- prediction。
    (4) SP-slice:即所谓的Switching P slice,为P-slice的一种特殊类型,用来串接两个不同bitrate的bitstream;
    (5) SI-slice: 即所谓的Switching I slice,为I-slice的一种特殊类型,除了用来串接两个不同content的bitstream外,也可用来执行随机存取(random access)来达到网络VCR的功能

    1. 宏块(Macroblock,MB)
      从上面结构图中可以看到片中包含宏块。那什么是宏块呢?

    宏块是视频信息的主要承载者。一个编码图像通常划分为多个宏块组成.包含着每一个像素的亮度和色度信息。视频解码最主要的工作则是提供高效的方式从码流中获得宏块中像素阵列。

    一个宏块 = 一个16*16的亮度像素 + 一个8×8Cb + 一个8×8Cr彩色像素块组成。(YCbCr 是属于 YUV 家族的一员,在YCbCr 中 Y 是指亮度分量,Cb 指蓝色色度分量,而 Cr 指红色色度分量)

    宏块分类:

    I宏块: 帧内预测

    P宏块: 利用前帧作为参考进行帧内预测,一个帧内编码的宏块可进一步作宏块的分割

    B宏块: 双向参考图像(前帧和后帧)进行帧内预测

    简单总结下帧和片和宏块的概念
    1帧 = 1个或n个片
    1片 = n个宏块
    1宏块 = 16x16yuv数据

    如下图所示



     

     

     

     

    宏块的结构如下图所示

     

     

     

     

    mb_type 确定该 MB 是帧内或帧间(P 或 B)编码模式,确定该 MB 分割的尺寸
    mb_pred 确定帧内预测模式(帧内宏块)确定表 0 或表 1 参考图 像,和每一宏块分割的差分编码的运动矢量(帧间宏块,除 8×8 宏块分割的帧内 MB)
    sub_mb_pred (只对 8×8MB 分割的帧内 MB)确定每一子宏块的子宏 块分割,每一宏块分割的表 0 和/或表 1 的参考图象;每一 宏块子分割的差分编码运动矢量。
    coded_block_pattern 指出哪个 8×8 块(亮度和彩色)包 编码变换系数
    mb_qp_delta 量化参数的改变值
    esidual 预测后对应于残差图象取样的编码变换系数

    I,P,B,IDR帧,DTS和PTS,GOP

    I帧: 帧内编码帧,I帧通常是每个GOP的第一帧,适度压缩,类似于图片jpg压缩一样的原理。大约可以得到6:1的压缩比。

    P帧: 前向预测编码帧,通过图像序列前面已经编码帧的时间冗余信息压缩,称为预测帧,大约可以得到20:1的压缩比

    B帧:双向预测内插编码帧,通过前帧和后帧的时间冗余信息压缩,也叫双向预测帧。大约可以得到50:1的压缩比

    IDR帧: I帧的一种特殊帧,一个序列的第一个图像叫做 IDR 图像(立即刷新图像)

    当解码器解码到 IDR 图像时,立即将参考帧队列清空,将已解码的数据全部输出或抛弃,重新查找参数集,开始一个新的序列。这样可以避免前一个序列出现重大错误的问题。

    DTS: (Decode Time Stamp) 用于视频的解码序列
    PTS: (Presentation Time Stamp)用于视频的显示序列。

    正因为有B帧这样的双向预测帧的存在,某一帧的解码序列和实际的显示序列是不一样的。如下图所示

     



     

     

     

     

     

    GOP: (Group of Picture)两个I帧之间形成的一组图片,就是GOP。一般为编码器设置参数的时候,必须设置gop_size的值,表示两个I帧之间的帧数目,相对来说GOP_size设置越小,画面质量越好。但是相应的容量越大。

    由于解码必须先获取到I帧,才能获得第一张图像,所以直播秒开的原理就是在CDN缓存一个GOP图片组,这样迅速解码出第一帧图。

    展开全文
  • h264视频压缩

    2016-10-13 10:07:56
    本程序实现了摄像头视频采集和格式转换。摄像头采集完生成yuyv格式,然后调用x264压缩264格式
  • 花了很长时间终于在mini2440下能够用UVC摄像头采集图像并且压缩了。这个只是一个初级的程序,帧率还是很慢的。只是实现了功能
  • 基于H.264压缩编码的ASF格式转换研究与实现
  • 使用FFmpeg将多幅图像压缩H264视频流
  • 一、V4L2采集YUYV视频数据 a) 打开V4L2设备并创建接收yuyv数据的文件 open_v4l2_device(const char *const devname) video_obj.v4l2_fd=open(devname,O_RDWR)//打卡v4l2...b) 设置视频格式,分辨率 set_v4l2_fmt

    一、V4L2采集YUYV视频数据

    a) 打开V4L2设备并创建接收yuyv数据的文件

    open_v4l2_device(const char *const devname)

    video_obj.v4l2_fd=open(devname,O_RDWR)//打卡v4l2设备

    fopen(name,"wb+")//创建yuyv数据接收文件

    b) 设置视频格式,分辨率

    set_v4l2_fmt(unsigned int format,unsigned int width,unsigned int height)

    video_obj.fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

    video_obj.fmt.fmt.pix.pixelformat = format;

    video_obj.fmt.fmt.pix.width = width;

    video_obj.fmt.fmt.pix.height = height;

    ioctl(video_obj.v4l2_fd, VIDIOC_S_FMT, &video_obj.fmt)

    c) 获取当前的格式和分辨率,查看设置是否生效

    struct v4l2_format fmt;

    fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

    ioctl(video_obj.v4l2_fd, VIDIOC_G_FMT, &fmt)

    d) 设置帧率

    set_v4l2_param(unsigned int num,unsigned int deno)

    struct v4l2_streamparm param;

    memset(&param, 0, sizeof(struct v4l2_streamparm));

    param.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

    param.parm.capture.timeperframe.numerator = num;

    param.parm.capture.timeperframe.denominator =deno;

    ioctl(video_obj.v4l2_fd, VIDIOC_S_PARM,&param)

    e) 获取帧率,查看设置是否生效

    get_v4l2_param(void)

    struct v4l2_streamparm param;

    memset(&param, 0, sizeof(struct v4l2_streamparm));

    param.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

    ioctl(video_obj.v4l2_fd, VIDIOC_G_PARM,&param)

    f) 申请V4L2帧缓存

    request_v4l2_buffer(unsigned int count)

    video_obj.buffers = calloc(count, sizeof(VideoBuffer));

    memset(&video_obj.req, 0, sizeof(video_obj.req));

    video_obj.req.count = count;

    video_obj.req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

    video_obj.req.memory = V4L2_MEMORY_MMAP;

    ioctl(video_obj.v4l2_fd, VIDIOC_REQBUFS, &video_obj.req)

    g) 内存映射摄像头的缓存

    for (numBufs = 0; numBufs < video_obj.req.count; numBufs++)

    {

    memset(&video_obj.buf, 0, sizeof(video_obj.buf));

    //数据流类型,永远都是V4L2_BUF_TYPE_VIDEO_CAPTURE

    video_obj.buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

    //存储类型:V4L2_MEMORY_MMAP(内存映射)或V4L2_MEMORY_USERPTR(用户指针)

    video_obj.buf.memory = V4L2_MEMORY_MMAP;

    video_obj.buf.index = numBufs;

    //使配置生效

    ioctl(video_obj.v4l2_fd, VIDIOC_QUERYBUF, &video_obj.buf)

    video_obj.buffers[numBufs].length = video_obj.buf.length;

    video_obj.buffers[numBufs].offset = (size_t)video_obj.buf.m.offset;

    //使用mmap函数将申请的缓存地址转换应用程序的绝对地址

    video_obj.buffers[numBufs].start =

    mmap(NULL,video_obj.buf.length,PROT_READ|PROT_WRITE,

    MAP_SHARED,video_obj.v4l2_fd,video_obj.buf.m.offset);

    //放入缓存队列

    ioctl(video_obj.v4l2_fd,VIDIOC_QBUF,&video_obj.buf)

    }

    h) 开始采集数据

    i. 获取一帧缓存数据

    start_v4l2_capture(void)

    type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

    ioctl(video_obj.v4l2_fd, VIDIOC_STREAMON, &type)

    pull_v4l2_frame_buffer(unsigned int index,unsigned char **start,unsigned int *len)

    video_obj.buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

    video_obj.buf.memory = V4L2_MEMORY_MMAP;

    video_obj.buf.index = index;

    ioctl(video_obj.v4l2_fd,VIDIOC_DQBUF,&video_obj.buf)

    *start = video_obj.buffers[index].start;

    *len = video_obj.buffers[index].length;

    ii. yuyv数据写入到文件(同时通过SDL2显示当前帧的数据)

    fwrite(photo,1,len,fd);

    fflush(fd);

    iii. 将该帧缓存放入到缓存池中

    push_v4l2_frame_buffer(unsigned int index)

    video_obj.buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

    video_obj.buf.memory = V4L2_MEMORY_MMAP;

    video_obj.buf.index = index;

    ioctl(video_obj.v4l2_fd, VIDIOC_QBUF, &video_obj.buf)

    i) 清理动态申请的数据

    release_v4l2_resource(void)

    for (numBufs = 0; numBufs < video_obj.req.count; numBufs++)

    munmap(video_obj.buffers[numBufs].start,video_obj.buf.length);

    free(video_obj.buffers);

    close(video_obj.v4l2_fd);

    二、通过SDL2显示采集到的yuyv原始数据

    a) 初始化sdl2需要用到的功能

    sdl2_init(int w,int h)

    SDL_Init(SDL_INIT_VIDEO|SDL_INIT_TIMER)

    b) 创建新的窗口

    sdl2_init(int w,int h)

    win = SDL_CreateWindow("Sam",0,0,w,h,SDL_WINDOW_SHOWN|SDL_WINDOW_RESIZABLE);

    c) 创建渲染器

    sdl2_init(int w,int h)

    renderer = SDL_CreateRenderer(win,-1,SDL_RENDERER_SOFTWARE);

    d) 设置渲染器的纹理

    sdl2_init(int w,int h)

    Texture=SDL_CreateTexture(renderer,SDL_PIXELFORMAT_YUY2,

    SDL_TEXTUREACCESS_STREAMING,w,h);

    e) 创建SDL2的事件处理线程

    pthread_create(&pid, NULL,event_loop,NULL)

    SDL_PollEvent(&event)

    f) 循环显示步骤1:更新渲染器纹理

    sdl2_refresh(void *pixels,int pitch)

    SDL_UpdateTexture(texture,NULL,pixels,pitch)

    g) 循环显示步骤2:清空渲染器

    sdl2_refresh(void *pixels,int pitch)

    SDL_UpdateTexture(texture,NULL,pixels,pitch)

    h) 循环显示步骤3:将纹理数据拷贝到渲染器

    sdl2_refresh(void *pixels,int pitch)

    SDL_RenderCopy(renderer,texture,NULL,NULL)

    i) 循环显示步骤4:显示视频

    sdl2_refresh(void *pixels,int pitch)

    SDL_RenderPresent(renderer);

    三、通过libX264压缩视频数据到H264

    a) 创建相关结构体,打开yuyv文件和将要保存h264数据的文件

    x264_nal_t* pNals = NULL;

    x264_t* pHandle   = NULL;

    x264_picture_t* pPic_in = (x264_picture_t*)malloc(sizeof(x264_picture_t));

    x264_picture_t* pPic_out = (x264_picture_t*)malloc(sizeof(x264_picture_t));

    x264_param_t* pParam = (x264_param_t*)malloc(sizeof(x264_param_t));

    FILE* fp_src  = fopen("x.yuv", "rb");

    FILE* fp_dst = fopen("x.h264", "wb");

    b) 给结构体x264_param_t赋予一些默认的参数,修改参数中的宽高和数据格式(因为录制的时候是采用V4L2_PIX_FMT_YUYV格式,宽高为640x480)

    x264_param_default(pParam);

    pParam->i_width   = width;

    pParam->i_height  = height;

    pParam->i_csp = csp;

    c) 设置profile

    x264_param_apply_profile(pParam, x264_profile_names[4]);

    d) 打开编码器

    pHandle = x264_encoder_open(pParam);

    e) 初始化帧数据的输入输出结构体

    x264_picture_init(pPic_out);

    x264_picture_alloc(pPic_in, csp, pParam->i_width, pParam->i_height);

    f) 根据视频数据计算出视频帧数

    fseek(fp_src,0,SEEK_END);

    switch(csp){

    case X264_CSP_I444:

    frame_num=ftell(fp_src)/(y_size*3);

    break;

    case X264_CSP_I420:

    frame_num=ftell(fp_src)/(y_size*3/2);

    break;

    case X264_CSP_I422:

    frame_num=ftell(fp_src)/(y_size*2);

    break;

    }

    fseek(fp_src,0,SEEK_SET);

    g) 循环编码步骤1:分离yuv分量

    h) 循环编码步骤2:编码一帧

    i) 循环编码步骤3:将编码后的数据写入到h264文件中

    上面三步骤包含的内容:

    for( i=0;i<frame_num;i++){

    switch(csp){

    case X264_CSP_I444:{

    fread(pPic_in->img.plane[0],y_size,1,fp_src);//Y

    fread(pPic_in->img.plane[1],y_size,1,fp_src);//U

    fread(pPic_in->img.plane[2],y_size,1,fp_src);//V

    break;}

    case X264_CSP_I420:{

    fread(pPic_in->img.plane[0],y_size,1,fp_src);//Y

    fread(pPic_in->img.plane[1],y_size/4,1,fp_src);//U

    fread(pPic_in->img.plane[2],y_size/4,1,fp_src);//V

    break;}

    case X264_CSP_I422:{

    /*

    Yuyv格式数据的存放方式为:(4X4像素)

    Y U Y V Y U Y V

    Y U Y V Y U Y V

    Y U Y V Y U Y V

    Y U Y V Y U Y V

    Y的个数为像素点的个数,

    实际上像素点的个数为y个数的两倍

    */

    int index = 0;

    int y_i  = 0 , u_i  = 0 , v_i = 0;

    for(index = 0 ; index < y_size*2 ;){

    fread(&pPic_in->img.plane[0][y_i++],1,1,fp_src);//Y

    index++;

    fread(&pPic_in->img.plane[1][u_i++],1,1,fp_src);//U

    index++;

    fread(&pPic_in->img.plane[0][y_i++],1,1,fp_src);//Y

    index++;

    fread(&pPic_in->img.plane[2][v_i++],1,1,fp_src);//V

    index++;

    }break;

    }

    }

    pPic_in->i_pts = i;

    x264_encoder_encode(pHandle, &pNals, &iNal, pPic_in, pPic_out);

    for ( j = 0; j < iNal; ++j)

    fwrite(pNals[j].p_payload, 1, pNals[j].i_payload, fp_dst);

    }

    j)     编码步骤4:将还残留在编码器中的数据flush out,并写入到文件

    while(1){

    ret = x264_encoder_encode(pHandle, &pNals, &iNal, NULL, pPic_out);  

    if(ret == 0)

    Break;

    for(j = 0;j < iNal; ++j)

    fwrite(pNals[j].p_payload, 1, pNals[j].i_payload, fp_dst);

    }

    k) 清理动态申请的资源

    x264_picture_clean(pPic_in);

    x264_encoder_close(pHandle);

    pHandle = NULL;

    free(pPic_in);

    free(pPic_out);

    free(pParam);

    fclose(fp_src);

    fclose(fp_dst);

     

    下面是两个项目的源码

    说明:

    1、V4L2采集数据和SDL2显示是在同一个项目中

    2、YUV数据文件通过libx264压缩为H264格式的文件为一个项目

    3、实验环境是vmwareubuntu系统,默认安装了SDL1.2,卸载了原来安装的1.2版本,重新编译安装SDL2.0

    4、需要安装Libx264

    5、项目中的源码大部分是参考网上各论坛的博客:

    http://blog.csdn.net/leixiaohua1020/article/details/42078645

    http://blog.csdn.net/yuanhubilie/article/details/37930429

    文件v4l2lib.c(数据采集和显示项目)

    //编译命令gcc v4l2lib.c -L/usr/local/SDL/lib/ -I/usr/local/SDL/include/SDL2 -lSDL2 -lpthread -o v4l2

    //SDL的库安装路径为/usr/local/SDL,根据自己安装路径修改

    #include <linux/videodev2.h>

    #include <fcntl.h>

    #include <unistd.h>

    #include <stdlib.h>

    #include <stdio.h>

    #include <string.h>

    #include <errno.h>

    #include <sys/types.h>

    #include <sys/stat.h>

    #include <fcntl.h>

    #include <sys/mman.h>

    #include <sys/select.h>

    #include <sys/time.h>

    #include <pthread.h>

    #include "SDL.h"

    #include <stdio.h>

    #include <time.h>

    //--------macro definition------

    #define MAX_DEV_NAME 32

    #define MAX_BUF 5

    #define VIDEO_REC

    #define WIDTH_PIX 640

    #define HEIGHT_PIX 480

    //--------structions defined here-------

    typedef struct VideoBuffer

    {

    unsigned char *start;

    size_t offset;

    size_t length;

    }VideoBuffer;

     

    typedef struct v4l2_param{

    char v4l2_devname[MAX_DEV_NAME];//设备名

    int v4l2_fd;//描述符号

    VideoBuffer *buffers;

    struct v4l2_requestbuffers req;

    struct v4l2_capability cap;

    struct v4l2_input input;

    struct v4l2_format fmt;

    struct v4l2_buffer buf;

    }VIDEO_T;

    //--------variable defined here-------

    static VIDEO_T video_obj;

    static pthread_t pid;

    static unsigned char state = 0;

    //--------SDL2----------

    static unsigned char inited = 0;

    static SDL_Window * win = NULL;

    static SDL_Renderer * renderer = NULL;

    static SDL_Texture * texture = NULL;

    static SDL_CommonEvent comm;

    static SDL_Event event;

     

    static int open_v4l2_device(const char *const devname)

    {

    //打开设备

    if(strlen(devname) >= MAX_DEV_NAME)

    {

    printf("device name fail:%s\n",devname);

    return -1;

    }

    else

    memset(&video_obj,0,sizeof(video_obj));

    video_obj.v4l2_fd = open(devname,O_RDWR);

    if(video_obj.v4l2_fd <= 0)

    {

    perror("open fail");

    return -1;

    }

    else

    printf("%s success\n",__func__);

    memcpy(video_obj.v4l2_devname,devname,strlen(devname));

    return 0;

    }

     

    static int set_v4l2_param(unsigned int num,unsigned int deno)

    {

    struct v4l2_streamparm param;

    memset(¶m, 0, sizeof(struct v4l2_streamparm));

    param.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

    param.parm.capture.timeperframe.numerator = num;

    param.parm.capture.timeperframe.denominator =deno;

    if(ioctl(video_obj.v4l2_fd, VIDIOC_S_PARM,¶m) < 0)

    {

    printf("%s fail\n",__func__);

    return -1;

    }

    else

    {

    printf("%s ok\n",__func__);

    return 0;

    }

    }

     

    static int get_v4l2_param(void)

    {

    struct v4l2_streamparm param;

    memset(¶m, 0, sizeof(struct v4l2_streamparm));

    param.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

    if(ioctl(video_obj.v4l2_fd, VIDIOC_G_PARM, ¶m) < 0)

    {

    perror("get param failed");

    return -1;

    }

    else

    {

    printf("%s:%d/%d\n",__func__,

    param.parm.capture.timeperframe.numerator,param.parm.capture.timeperframe.denominator);

    return 0;

    }

    }

     

    static int set_v4l2_fmt(unsigned int format,

    unsigned int width,unsigned int height)

    {

    //设置视频格式

    memset(&video_obj.fmt,0,sizeof(video_obj.fmt));

    //视频数据流类型,永远都是V4L2_BUF_TYPE_VIDEO_CAPTURE

    video_obj.fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

    //视频源的格式为JPEGYUN4:2:2RGB  V4L2_PIX_FMT_RGB565  V4L2_PIX_FMT_YUV565

    video_obj.fmt.fmt.pix.pixelformat = format;

    //video_obj.fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;

    //设置视频宽度

    video_obj.fmt.fmt.pix.width = width;

    //设置视频高度

    video_obj.fmt.fmt.pix.height = height;

    if (ioctl(video_obj.v4l2_fd, VIDIOC_S_FMT, &video_obj.fmt) < 0)//使配置生效

    {

    perror("set format failed");

    return -1;

    }

    else

    printf("%s success[format:%X w:%d h:%d]\n",__func__,format,width,height);

     

    struct v4l2_format fmt;

    fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

    if (ioctl(video_obj.v4l2_fd, VIDIOC_G_FMT, &fmt) < 0)

    {

    perror("set format failed");

    return -1;

    }

    else

    printf("%s get success[format:%X w:%d h:%d]\n",

    __func__,fmt.fmt.pix.pixelformat,fmt.fmt.pix.width,fmt.fmt.pix.height);

    return 0;

    }

     

    static int request_v4l2_buffer(unsigned int count)

    {

    //申请帧缓冲

    video_obj.buffers = calloc(count, sizeof(VideoBuffer));

    memset(&video_obj.req, 0, sizeof(video_obj.req));

    //缓存数量,即可保存的图片数量

    video_obj.req.count = count;

    //数据流类型,永远都是V4L2_BUF_TYPE_VIDEO_CAPTURE

    video_obj.req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

    //存储类型:V4L2_MEMORY_MMAPV4L2_MEMORY_USERPTR

    video_obj.req.memory = V4L2_MEMORY_MMAP;

    //使配置生效

    if (ioctl(video_obj.v4l2_fd, VIDIOC_REQBUFS, &video_obj.req) == -1)

    {

    perror("request buffer error \n");

    return -1;

    }

    else

    printf("%s success[request %d buffers]\n",__func__,count);

    return 0;

    }

     

    static int mmap_v4l2_buffer(void)

    {

    //VIDIOC_REQBUFS获取内存转为物理空间

    int numBufs;

    for (numBufs = 0; numBufs < video_obj.req.count; numBufs++)

    {

    memset(&video_obj.buf, 0, sizeof(video_obj.buf));

    //数据流类型,永远都是V4L2_BUF_TYPE_VIDEO_CAPTURE

    video_obj.buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

    //存储类型:V4L2_MEMORY_MMAP(内存映射)或V4L2_MEMORY_USERPTR(用户指针)

    video_obj.buf.memory = V4L2_MEMORY_MMAP;

    video_obj.buf.index = numBufs;

    //使配置生效

    if (ioctl(video_obj.v4l2_fd, VIDIOC_QUERYBUF, &video_obj.buf) < 0)

    {

    perror("VIDIOC_QUERYBUF");

    return -1;

    }

    //printf("request buf %d success\n",numBufs);

    video_obj.buffers[numBufs].length = video_obj.buf.length;

    video_obj.buffers[numBufs].offset = (size_t)video_obj.buf.m.offset;

    //使用mmap函数将申请的缓存地址转换应用程序的绝对地址

    video_obj.buffers[numBufs].start = mmap(NULL,video_obj.buf.length,

    PROT_READ|PROT_WRITE,MAP_SHARED,video_obj.v4l2_fd,video_obj.buf.m.offset);

    if (video_obj.buffers[numBufs].start == MAP_FAILED)

    {

    perror("buffers error");

    return -1;

    }

    //printf("mmap buf 0x%p lenght:%d success\n",video_obj.buffers[numBufs].start,video_obj.buf.length);

    //放入缓存队列

    if (ioctl(video_obj.v4l2_fd,VIDIOC_QBUF,&video_obj.buf) < 0)

    {

    printf("VIDIOC_QBUF");

    return -1;

    }

    }

    printf("%s success\n",__func__);

    return 0;

    }

     

    static int start_v4l2_capture(void)

    {

    //开始视频显示

    enum v4l2_buf_type type;

    //数据流类型,永远都是V4L2_BUF_TYPE_VIDEO_CAPTURE

    type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

    if (ioctl(video_obj.v4l2_fd, VIDIOC_STREAMON, &type) < 0)

    {

    perror("VIDIOC_STREAMON");

    return -1;

    }

    printf("%s stream on success\n",__func__);

    return 0;

    }

     

    static int pull_v4l2_frame_buffer(unsigned int index , unsigned char **start , unsigned int *len)

    {

    video_obj.buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; //取得原始采集数据

    video_obj.buf.memory = V4L2_MEMORY_MMAP; //存储类型:V4L2_MEMORY_MMAP(内存映射)或V4L2_MEMORY_USERPTR(用户指针)

    if(video_obj.req.count <= index)

    return -1;

    video_obj.buf.index = index; //读取缓存中的第几帧

    if (ioctl(video_obj.v4l2_fd,VIDIOC_DQBUF,&video_obj.buf) < 0)

    {

    perror("VIDIOC_DQBUF");

    return -1;

    }

    *start = video_obj.buffers[index].start;

    *len = video_obj.buffers[index].length;

    return 0;

    }

     

    static int push_v4l2_frame_buffer(unsigned int index)

    {

    video_obj.buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; //取得原始采集数据

    video_obj.buf.memory = V4L2_MEMORY_MMAP; //存储类型:V4L2_MEMORY_MMAP(内存映射)或V4L2_MEMORY_USERPTR(用户指针)

    if(video_obj.req.count <= index)

    return -1;

    video_obj.buf.index = index; //第几帧放入缓存

    //获取下一帧视频数据

    if (ioctl(video_obj.v4l2_fd, VIDIOC_QBUF, &video_obj.buf) < 0)

    {

    perror("VIDIOC_QBUF");

    return -1;

    }

    return 0;

    }

     

    static void release_v4l2_resource(void)

    {

    int numBufs;

    for (numBufs = 0; numBufs < video_obj.req.count; numBufs++)

    munmap(video_obj.buffers[numBufs].start,video_obj.buf.length);

    free(video_obj.buffers);

    close(video_obj.v4l2_fd);

    printf("%s\n",__func__);

    }

     

    static int sdl2_init(int w,int h)

    {

    if(SDL_Init(SDL_INIT_VIDEO|SDL_INIT_TIMER) == -1)

    {

    printf("SDL_Init fail!");

    return -1;

    }

    else

    printf("SDL_Init success\n");

    /*

    title  :窗口标题

    x :窗口位置x坐标。也可以设置为SDL_WINDOWPOS_CENTEREDSDL_WINDOWPOS_UNDEFINED

    y :窗口位置y坐标。同上。

    w    :窗口的宽

    h :窗口的高

    flags :支持下列标识。包括了窗口的是否最大化、最小化,能否调整边界等等属性。

       ::SDL_WINDOW_FULLSCREEN,    ::SDL_WINDOW_OPENGL,

       ::SDL_WINDOW_HIDDEN,    ::SDL_WINDOW_BORDERLESS,

       ::SDL_WINDOW_RESIZABLE,    ::SDL_WINDOW_MAXIMIZED,

       ::SDL_WINDOW_MINIMIZED,    ::SDL_WINDOW_INPUT_GRABBED,

       ::SDL_WINDOW_ALLOW_HIGHDPI.

    */

    win = SDL_CreateWindow("Sam",0,0,w,h,SDL_WINDOW_SHOWN|SDL_WINDOW_RESIZABLE);

    if(win == NULL)

    {

    printf("SDL_CreateWindow fail\n");

    return -1;

    }

    else

    {

    printf("SDL_CreateWindow success\n");

    }

    //创建渲染器

    /*

    window    渲染的目标窗口。

    index   :打算初始化的渲染设备的索引。设置-1”则初始化默认的渲染设备。

    flags    :支持以下值(位于SDL_RendererFlags定义中)

    SDL_RENDERER_SOFTWARE :使用软件渲染

    SDL_RENDERER_ACCELERATED :使用硬件加速

    SDL_RENDERER_PRESENTVSYNC:和显示器的刷新率同步

    SDL_RENDERER_TARGETTEXTURE :不太懂

    */

    renderer = SDL_CreateRenderer(win,-1,SDL_RENDERER_SOFTWARE);

    if(renderer == NULL)

    {

    printf("SDL_CreateRenderer fail\n");

    return -1;

    }

    else

    {

    printf("SDL_CreateRenderer success\n");

    }

    /*

    参数的含义如下。

    renderer:目标渲染器。

    format :纹理的格式。后面会详述。

    access :可以取以下值(定义位于SDL_TextureAccess中)

    SDL_TEXTUREACCESS_STATIC  :变化极少

    SDL_TEXTUREACCESS_STREAMING    :变化频繁

    SDL_TEXTUREACCESS_TARGET    :暂时没有理解

    w :纹理的宽

    h :纹理的高

    */

    texture = SDL_CreateTexture(renderer,SDL_PIXELFORMAT_YUY2,SDL_TEXTUREACCESS_STREAMING,w,h);

    if(texture == NULL)

    {

    printf("SDL_CreateTexture fail\n");

    return -1;

    }

    else

    {

    printf("SDL_CreateTexture success\n");

    }

    return 0;

    }

     

    static void *event_loop(void *param)

    {

    printf("%s begin time:%d\n",__func__,SDL_GetTicks());

    while(1)

    {

    if(SDL_PollEvent(&event) > 0 &&

    (comm.type != event.common.type || comm.timestamp != event.common.timestamp))

    {

    comm.type = event.common.type;

    comm.timestamp = event.common.timestamp;

    switch(event.type)

    {  

    case SDL_QUIT:

    printf("SDL_WINDOWEVENT\n");state = 1;return NULL;

    case SDL_WINDOWEVENT:

    printf("SDL_WINDOWEVENT\n");break;

    case SDL_SYSWMEVENT:

    printf("SDL_SYSWMEVENT\n");break;

    case SDL_KEYDOWN:

    printf("SDL_KEYDOWN\n");break;

    case SDL_KEYUP:

    printf("SDL_KEYUP\n");break;

    case SDL_TEXTEDITING:

    printf("SDL_TEXTEDITING\n");break;

    case SDL_TEXTINPUT:

    printf("SDL_TEXTINPUT\n");break;

    case SDL_KEYMAPCHANGED:

    printf("SDL_KEYMAPCHANGED\n");break;

    case SDL_MOUSEMOTION:

    printf("SDL_MOUSEMOTION\n");break;

    case SDL_MOUSEBUTTONDOWN:

    printf("SDL_MOUSEBUTTONDOWN\n");break;

    case SDL_MOUSEBUTTONUP:

    printf("SDL_MOUSEBUTTONUP\n");break;

    case SDL_MOUSEWHEEL:

    printf("SDL_MOUSEWHEEL\n");break;

    default:

    printf("%X\n",event.type);

    break;

    }

    }

    }

    printf("%s end time:%d\n",__func__,SDL_GetTicks());

    return NULL;

    }

     

    static int sdl2_refresh(void *pixels,int pitch)

    {

    if(inited == 0)

    {

    if(sdl2_init(WIDTH_PIX,HEIGHT_PIX))

    return -1;

    inited = 1;

    if(pthread_create(&pid, NULL,event_loop,NULL) != 0)

    {

    printf("pthread_create fail\n");

    return;

    }

    }

    /*

    参数的含义如下。

    texture:目标纹理。

    rect:更新像素的矩形区域。设置为NULL的时候更新整个区域。

    pixels:像素数据。

    pitch:一行像素数据的字节数。

    */

    if(SDL_UpdateTexture(texture,NULL,pixels,pitch) != 0)

    {

    printf("SDL_UpdateTexture fail\n");

    return -1;

    }

    //清空渲染

        SDL_RenderClear(renderer);

    /*

    参数的含义如下。

    renderer:渲染目标。

    texture:输入纹理。

    srcrect:选择输入纹理的一块矩形区域作为输入。设置为NULL的时候整个纹理作为输入。

    dstrect:选择渲染目标的一块矩形区域作为输出。设置为NULL的时候整个渲染目标作为输出。

    */

    if(SDL_RenderCopy(renderer,texture,NULL,NULL) != 0)

    {

    printf("SDL_RenderCopy fail\n");

    return -1;

    }

    SDL_RenderPresent(renderer);

    return 0;

    }

     

    static void sdl2_uninit(void)

    {

    SDL_DestroyTexture(texture);

    SDL_DestroyRenderer(renderer);

    SDL_DestroyWindow(win);

    SDL_Quit();

    printf("SDL uninit\n");

    }

     

    static int generate_yuv_name(char *name,int maxlen)

    {

    //实例化time_t结构

    time_t now;

    time(&now);

    //localtime函数把从time取得的时间now换算成你电脑中的时间(就是你设置的地区)

    snprintf(name,maxlen,"%d.yuv",2);

    printf("%s:%s\n",__func__,name);

    return 0;

    }

     

    void main(int argc ,char *argv[])

    {

    int index = 0;

    int cnt = 0;

    unsigned char *photo = NULL;

    unsigned int len = 0;

    char name[64] = {0};

    if(open_v4l2_device(argv[1])!=0)

    return;

    #ifdef VIDEO_REC

    generate_yuv_name(name,sizeof(name));

    FILE * fd = fopen(name,"wb+");

    if(fd == NULL)

    {

    printf("open fail:%s\n",name);

    return;

    }

    #endif

    if(set_v4l2_fmt(V4L2_PIX_FMT_YUYV,WIDTH_PIX,HEIGHT_PIX))

    return ;

    if(set_v4l2_param(1,30))

    return ;

    if(get_v4l2_param())

    return ;

    if(request_v4l2_buffer(MAX_BUF))

    return ;

    if(mmap_v4l2_buffer())

    return ;

    if(start_v4l2_capture())

    return ;

    while(1)

    {

    //printf("cnt:%d\n",cnt++);

    if(pull_v4l2_frame_buffer(index,&photo,&len))

    break;

    #ifdef VIDEO_REC

    {

    int l = fwrite(photo,1,len,fd);

    if(l != (HEIGHT_PIX*WIDTH_PIX*2))

    {

    printf("write fail:%s [%d]\n",name,l);

    fclose(fd);

    break;

    }

    fflush(fd);

    }

    #endif

    if(sdl2_refresh(photo,WIDTH_PIX*2))

    break;

    push_v4l2_frame_buffer(index);

    index++;

    if(index == MAX_BUF)

    index = 0;

    if(state == 1)

    break;

    usleep(1000*30);

    }

    sdl2_uninit();

    release_v4l2_resource();

    }

     

     

    文件x264.c(视频数据压缩项目)

    //编译命令:gcc x264.c -L/usr/local/x264/lib/ -I/usr/local/x264/include/ -lx264 -o x264

    //libx264库安装在/usr/local/x264/,需要更具自己的安装目录修改命令

    #include <stdio.h>

    #include <stdlib.h>

    #include "stdint.h"

    #if defined ( __cplusplus)

    extern "C"

    {

    #include "x264.h"

    };

    #else

    #include "x264.h"

    #endif

    int main(int argc, char** argv)  

    {  

    int ret;

    int y_size;

    int i,j;

    //Encode 50 frame

    //if set 0, encode all frame

    int frame_num = 0;

    const int csp = X264_CSP_I422;

    int width = 640;

    int height = 480;

    int iNal = 0;

    x264_nal_t* pNals = NULL;

    x264_t* pHandle   = NULL;

    x264_picture_t* pPic_in = (x264_picture_t*)malloc(sizeof(x264_picture_t));

    x264_picture_t* pPic_out = (x264_picture_t*)malloc(sizeof(x264_picture_t));

    x264_param_t* pParam = (x264_param_t*)malloc(sizeof(x264_param_t));

    FILE* fp_src  = fopen("2.yuv", "rb");

    FILE* fp_dst = fopen("x.h264", "wb");

    //Check

    if(fp_src==NULL||fp_dst==NULL)

    {

    printf("Error open files.\n");

    return -1;

    }

    x264_param_default(pParam);

    pParam->i_width   = width;

    pParam->i_height  = height;

    //Param

    /*

    pParam->i_log_level  = X264_LOG_DEBUG;

    pParam->i_threads = X264_SYNC_LOOKAHEAD_AUTO;

    pParam->i_frame_total = 0;

    pParam->i_keyint_max = 10;

    pParam->i_bframe  = 0;

    pParam->b_open_gop  = 0;

    pParam->i_bframe_pyramid = 0;

    pParam->rc.i_qp_constant=0;

    pParam->rc.i_qp_max=0;

    pParam->rc.i_qp_min=0;

    pParam->i_bframe_adaptive = X264_B_ADAPT_TRELLIS;

    pParam->i_fps_den = 1;

    pParam->i_fps_num = 25;

    pParam->i_timebase_den = pParam->i_fps_num;

    pParam->i_timebase_num = pParam->i_fps_den;

    */

    pParam->i_csp = csp;

    x264_param_apply_profile(pParam, x264_profile_names[4]);

    pHandle = x264_encoder_open(pParam);

    x264_picture_init(pPic_out);

    x264_picture_alloc(pPic_in, csp, pParam->i_width, pParam->i_height);

    y_size = pParam->i_width * pParam->i_height;

    printf("w:%d h:%d\r\n",pParam->i_width,pParam->i_height);

    //detect frame number

    if(frame_num==0)

    {

    fseek(fp_src,0,SEEK_END);

    switch(csp)

    {

    case X264_CSP_I444:

    frame_num=ftell(fp_src)/(y_size*3);

    break;

    case X264_CSP_I420:

    frame_num=ftell(fp_src)/(y_size*3/2);

    break;

    case X264_CSP_I422:

    frame_num=ftell(fp_src)/(y_size*2);

    break;

    default:

    printf("Colorspace Not Support.\n");

    return -1;

    }

    fseek(fp_src,0,SEEK_SET);

    }

    printf("frame_num:%d y_size:%d\r\n",frame_num,y_size);

    //Loop to Encode  

    for( i=0;i<frame_num;i++)

    {

    switch(csp)

    {

    case X264_CSP_I444:

    {

    fread(pPic_in->img.plane[0],y_size,1,fp_src);   //Y

    fread(pPic_in->img.plane[1],y_size,1,fp_src);   //U

    fread(pPic_in->img.plane[2],y_size,1,fp_src);   //V

    break;

    }

    case X264_CSP_I420:

    {

    fread(pPic_in->img.plane[0],y_size,1,fp_src); //Y

    fread(pPic_in->img.plane[1],y_size/4,1,fp_src); //U

    fread(pPic_in->img.plane[2],y_size/4,1,fp_src); //V

    break;

    }

    case X264_CSP_I422:

    {

    int index = 0;

    int y_i  = 0 , u_i  = 0 , v_i = 0;

    for(index = 0 ; index < y_size*2 ;)

    {

    fread(&pPic_in->img.plane[0][y_i++],1,1,fp_src);//Y

    index++;

    fread(&pPic_in->img.plane[1][u_i++],1,1,fp_src);//U

    index++;

    fread(&pPic_in->img.plane[0][y_i++],1,1,fp_src);//Y

    index++;

    fread(&pPic_in->img.plane[2][v_i++],1,1,fp_src);//V

    index++;

    }

    break;

    }

    default:

    {

    printf("Colorspace Not Support.\n");

    return -1;

    }

    }

    pPic_in->i_pts = i;

    ret = x264_encoder_encode(pHandle, &pNals, &iNal, pPic_in, pPic_out);

    if (ret< 0)

    {

    printf("Error.\n");

    return -1;

    }

    for ( j = 0; j < iNal; ++j)

    fwrite(pNals[j].p_payload, 1, pNals[j].i_payload, fp_dst);

    }

    //flush encoder

    while(1)

    {

    ret = x264_encoder_encode(pHandle, &pNals, &iNal, NULL, pPic_out);  

    if(ret==0)

    break;

    //printf("Flush 1 frame.\n");

    for(j = 0;j < iNal; ++j)

    fwrite(pNals[j].p_payload, 1, pNals[j].i_payload, fp_dst);

    }

    x264_picture_clean(pPic_in);

    x264_encoder_close(pHandle);

    pHandle = NULL;

    free(pPic_in);

    free(pPic_out);

    free(pParam);

    fclose(fp_src);

    fclose(fp_dst);

    return 0;

    }

     这里有相关资源的下载:

    http://download.csdn.net/download/xushan239/10199075

    http://download.csdn.net/download/xushan239/10190449


     

     

    展开全文
  • H264视频压缩编码标准

    2014-02-11 15:37:44
    H.264编解码器特点 \H.264解码器\ H.264/AVC的结构
  • H.264视频压缩标准

    万次阅读 2016-11-03 10:17:51
     H.264是最新的视频压缩标准,它也称为MPEG-4 Part 10或AVC(高级视频编码)。据预测,在未来几年内H.264将成为行业首选的视频标准。  H.264是一个需要许可证才能使用的开放标准,可支持最当今市场上最高效的视频...
    一.简介
    
           H.264是最新的视频压缩标准,它也称为MPEG-4 Part 10或AVC(高级视频编码)。据预测,在未来几年内H.264将成为行业首选的视频标准。
           H.264是一个需要许可证才能使用的开放标准,可支持最当今市场上最高效的视频压缩技术。在不影响图像质量的情况下,与采用M-JPEG和MPEG-4 Part 2标准相比,H.264编码器可使数字视频文件的大小分别减少80%和50%以上。这意味着视频文件所需的网络带宽和存储空间将大大降低。或者从另一个角度来说,在某一特定比特率下,视频图像质量将得到显著提高。
           H.264由电信和IT行业的标准化组织联合制定,与先前标准相比,H.264有望得到更广泛的应用。
           H.264已经应用于手机和数字视频播放器等新一代电子产品中,并且迅速获得广大最终用户的青睐。在线视频存储公司和电信公司等服务提供商也开始采用H.264标准。
           在视频监控行业,H.264将很可能以最快速度应用于那些需要高帧速和高分辨率的监控场所,例如高速公路、机场和娱乐场所等。对于这些监控场所而言,使用30/25 fps(NTSC/PAL)的帧速已经成为一种通行的标准。然而,由于H.264能够减少带宽和存储需求,具有显著的经济性,因此可以帮助企业最大限度节省成本。

           此外,由于H.264这种极其高效的压缩技术能够在不影响图像质量的前提下,对大文件进行大比例压缩并降低比特率,因此有望提高百万像素摄像机的普及速度。然而,事物都有其两面性。虽然H.264能够节省网络带宽和存储成本,然而它却对网络摄像机和显示终端提出了更高的性能要求。


    二.H.264的发展
           H.264是ITU-T的视频编码专家组(VCEG)和ISO/IEC运动图像专家组(MPEG)联合制定的新一代视频压缩标准。ITU-T是一个代表国际电信联盟协调制定电信标准的部门。ISO是指国际标准化组织。IEC是指国际电工委员会,负责制定所有电子、电气和相关技术的标准。H.264是ITU-T所使用的名称,而ISO/IEC将其命名为MPEG-4 Part 10/AVC,因为它代表的是MPEG-4系列标准中的一个新标准。MPEG-4系列标准包括了MPEG-4 Part 2等标准,MPEG-4 Part 2是一个应用于基于IP的视频编码器和网络摄像机的标准。
           为了解决先前视频压缩标准中存在的不足,H.264的目标是支持:
    > 高效压缩,在某一特定的视频质量下,与采用任何其它视频标准相比,可以使比特率平均降低50%。
    > 更强大的容错能力,能够纠正各种网络的传输错误
    > 低时延功能,并能够在更高时延的情况下提供更高质量的图像
    > 通过简单的句法规范简化实施
    > 精确匹配解码,严格规定了编码器和解码器如何进行数值计算,以避免错误累积
            此外,H.264还能够灵活地支持有着不同比特率要求的各种监控应用。例如,在娱乐视频应用(包括广播、卫星电视、有线电视和DVD)中,H.264能够以高时延实现1-10Mbit/秒的性能。而对于电信服务来说,H.264能够以低时延实现低于1Mbit/秒的比特率。

    三.视频压缩的工作原理
           视频压缩通过减少和去除冗余视频数据的方式,达到有效发送和存储数字视频文件的目的。在压缩过程中,需要应用压缩算法对源视频进行压缩以创建压缩文件,以便进行传输和存储。要想播放压缩文件,则需要应用相反的解压缩算法对视频进行还原,还原后的视频内容与原始的源视频内容几乎完全相同。压缩、发送、解压缩和显示文件所需的时间称为延时。在相同处理能力下,压缩算法越高级,延时就越长。
           视频编解码器(编码器/解码器)是指两个协同运行的压缩-解压算法。使用不同标准的视频编解码器通常彼此之间互不兼容;也就是说,使用一种标准进行压缩的视频内容无法使用另外一种标准进行解压缩。例如,MPEG-4 Part 2解码器就不能与H.264编码器协同运行。这是因为一种算法无法正确地对另外一个算法的输出信号进行解码,然而我们可以在同一软件或硬件中使用多种不同的算法,以支持对多种格式的文件进行压缩。
           由于不同的视频压缩标准会使用不同的方法来减少数据量,因此压缩结果在比特率、质量和延时方面也各不相同。
           此外,由于编码器的设计者可能会选择使用某个标准所定义的不同工具集,因此,即使是使用相同压缩标准的编码器之间,其压缩结果也可能会存在差异。不过,只要编码器的输出信号符合标准的格式以及解码器的要求,就可以采用不同的实施方式。这是非常有利的,因为不同的实施方式可实现不同的目标,满足不同的预算要求。对用于管理光介质存储的非实时专业软件编码器来说,应该能够比用于视频会议的集成在手持设备中的实时硬件编码器提供质量更高的编码视频。因此,即使是某个指定的标准也无法保证提供指定的比特率或质量。而且,如果不事先确定实施方式,一个标准就无法与其它标准进行正确的性能对比,甚至也无法与同一标准的其它实施方式进行正确的性能对比。
          与编码器不同,解码器必须实施某个标准的所有必需部分,才能对符合标准的比特流进行解码。这是因为标准中明确规定了解压缩算法应如何对压缩视频的每个比特进行还原。
           下图是在相同图像质量水平下,采用下列视频标准的比特率对比:M-JPEG,MPEG-4 Part 2(无运动补 偿)、MPEG-4 Part 2(有运动补偿)和H.264(基准类)。


    图1.对于视频序列样本来说,使用H.264编码器能够比使用有运动补偿的MPEG-4编码器降低50%的比特率(bps)。在没有运动补偿的情况下,H.264编码器的效率至少比MPEG-4编码器高3倍,比M-JPEG编码器高6倍。

    四.H.264类别和等级
           参与制定H.264标准的联合组织致力于创建一个简单明了的解决方案,最大限度地限制选项和特性的数量。和其它视频标准一样,H.264标准的一个重要方面是通过类别(算法特性集)和等级(性能等级)中提供的功能,以最佳的方式支持常见应用和通用格式。
           H.264有7个类别,每个类别都针对某一类特定的应用。此外,每个类别都定义了编码器能够使用哪些特性集,并限制了解码器在实施方面的复杂性。
           网络摄像机和视频编码器最有可能使用的是基准类别,此类别主要针对计算资源有限的应用。对于嵌入在网络视频产品中的实时编码器来说,在特定的可用性能下,基准类别最为适用。此类别能够实现低延时,这对监控视频来说是一个很重要的要求,而且对于支持PTZ网络摄像机实现实时的平移/倾斜/缩放(PTZ)控制来说尤为重要。

           H.264分为11个功能等级,对性能、带宽和内存需求进行了限制。每个等级都规定了从QCIF到HDTV等各种分辨率所对应的比特率和编码速率(每秒宏块数)。分辨率越高,要求的等级就越高。


    五.帧的基本知识

           根据H.264的不同类别,编码器会使用不同类型的帧,例如I帧、P帧和B帧。

           I帧(帧内编码帧)是一种自带全部信息的独立帧,无需参考其它图像便可独立进行解码。视频序列中的第一个帧始终都是I帧。如果所传输的比特流遭到破坏,则需要将I帧用作新查看器的起始点或重新同步点。I帧可以用来实现快进、快退以及其它随机访问功能。如果新的客户端将参与查看视频流,编码器将以相同的时间间隔或者根据要求自动插入I帧。I帧的缺点在于它们会占用更多的数据位,但从另一方面看,I帧不会产生可觉察的模糊现象。

           P帧(帧间预测编码帧)需要参考前面的I帧和/或P帧的不同部分才能进行编码。与I帧相比,P帧通常占用更少的数据位,但其缺点是,由于P帧对前面的P和I参考帧有着复杂的依赖性,因此对传输错误非常敏感。

           B帧(双向预测编码帧)需要同时以前面的帧和后面的帧作为参考帧。


    图2.带有I帧、B帧和P帧的典型视频序列。P帧只需要参考前面的I帧或P帧,而B帧则需要同时参考前面和后面的I帧或P帧。
           当视频解码器逐个帧地对比特流进行解码以便重构视频时,必须始终从I帧开始解码。如果使用了P帧和B帧,则必须与参考帧一起解码。

           在H.264基准类(baseline)中,仅使用I帧和P帧。由于基准类没有使用B帧,所以可以实现低延时,因此是网络摄像机和视频编码器的理想选择。


    六.减少数据量的基本方法
           通过各种方法在一个图像帧内或者在一系列帧之间减少视频数据量。
           在某个图像帧内,只需要删除不必要的信息就可以减少数据量,但这样做会导致图像的分辨率下降。
           在一系列的帧内,可以通过差分编码这样的方法来减少视频数据量,包括H.264在内的大多数视频压缩标准都采用这种方法。在差分编码中,会将一个帧与参考帧(即前面的I帧或P帧)进行对比,然后只对那些相对于参考帧来说发生了变化的像素进行编码。通过这种方法,可以降低需要进行编码和发送的像素值。

       

    图3.对M-JPEG格式来说,上述序列中的三个图像分别作为独立的图像(I帧)进行编码和发送,彼此之间互不依赖。


    图4.对差分编码(包括H.264在内的大多数视频压缩标准都采用这种方法)来说,只有第一个图像(I帧)是将全帧图像信息进行编码。在后面的两个图像(P帧)中,其静态部分(即房子)将参考第一个图像,而仅对运动部分(即正在跑步的人)使用运动矢量进行编码,从而减少发送和存储的信息量。

           如果是根据像素块(宏块)而不是单个的像素来检测差别并进行差分编码,还可以进一步减少需要编码的信息量;因此,可以对更大的区域进行对比,而只需对那些存在重大差别的块进行编码。此外,对发生更改的区域位置进行标记的相关开销也将大大降低。

           然而,如果视频中存在大量物体运动的话,差分编码将无法显著减少数据量。这时,可以采用基于块的运动补偿技术。基于块的运动补偿考虑到视频序列中构成新帧的大量信息都可以在前面的帧中找到,但可能会在不同的位置上。所以,这种技术将一个帧分为一系列的宏块。然后,通过在参考帧中查找匹配块的方式,逐块地构建或者“预测”一个新帧(例如P帧)。如果发现匹配的块,编码器只需要对参考帧中发现匹配块的位置进行编码。与对块的实际内容进行编码相比,只对运动矢量进行编码可以减少所占用的数据位。


    图5.对M-JPEG格式来说,上述序列中的三个图像分别作为独立的图像(I帧)进行编码和发送,彼此之间互不依赖。


    七.H.264的效率

           H.264将视频压缩技术提升到一个新的高度。在H.264中,将通过新的高级帧内预测方法对I帧进行编码。这种方法通过对帧中每个宏块内较小的像素块进行连续预测,可以大大减少I帧所占的数据位并保持较高的质量。这一点可通过在与进行帧内编码的新4×4像素块相邻接的前几个编码像素中,寻找匹配的像素来实现。通过重复利用已编码的像素值,可以极大地减少需要编码的位数。新的帧内预测功能是H.264技术的关键部分,实验证明,这种方法非常有效。与只使用I帧的M-JPEG视频流相比,只使用I帧的H.264视频流的文件大小要小得多。


    图6.在由16个像素块构成的宏块中,对其中1个像素块内的4×4像素进行编码时,帧内预测所采用的几种模式的图示。在宏块的16个像素块中,每个像素块都可以使用不同的模式进行编码。

    图7.以上是H.264帧内预测方法的效率图示。通过这种方法,帧内预测图像将“免费”发送。只需对残留图像和帧内预测
    模式进行编码,就可以生成输出图像。
           对P帧和B帧进行编码时所采用的基于块的运动补偿,在H.264中也得到了改进。H.264编码器可以在一个或多个参考帧的少数或众多区域内,以低至子像素的精度搜索匹配的块。为了提高匹配率,可以对块的大小和形状进行调整。在参考帧中,对于找不到匹配块的区域,将会使用帧内编码的宏块。H.264基于块的运动补偿具有高度的灵活性,非常适合人群比较拥挤的监控场所,因为它能够保证较高的质量,以满足严格的应用要求。运动补偿是视频编码器要求最严格的一个方面,H.264编码器实施运动补偿的不同方式以及其实施程度,将会影响视频压缩的效率。
           对于H.264,通过使用环内去块效应滤波器,可以减少在使用M-JPEG和MPEG标准(而不是H.264标准)的
    高度压缩视频中通常出现的图像模糊现象。此过滤器能够通过自适应强度使块边缘变得平滑,从而确保

    输出几乎完美无缺的解压缩视频。


    图8.从右边的图中我们可以看到,在应用了去块效应滤波器之后,左图中高度压缩图像的块状效应已经大大降低。


    八.总结
           H.264代表着视频压缩技术的一个重大飞跃。由于该技术具有更精确的预测能力和更高的容错能力,因此可实现更高的压缩效率。它将有可能推动视频编码器进一步向前发展,从而能够在同样的比特率下提供更高质量的视频流、更高的帧速以及更高的分辨率(与先前的标准相比);或者反过来说,能够在同样的视频质量下降低比特率。
           H.264是ITU、ISO和IEC首次在视频压缩方面联合制定的一个通用国际标准。由于其具有高度的灵活性,H.264已广泛应用于各种领域中,例如:高清DVD(例如蓝光)、数字视频广播(包括高清TV)、在线视频存储(例如YouTube)、3G移动电话、软件(例如QuickTime、Flash和苹果计算机公司的MacOS X操作系统等),以及家用电视游戏机(例如PlayStation 3等)。在众多行业的支持下,以及在为满足个人消费者和专业用户需求的应用的推动下,H.264有望取代当今市场中使用的其它压缩标准和方法。

           随着H.264格式更加广泛地应用于网络摄像机、视频编码器和视频管理软件,系统设计商和集成商将需要确保他们所选择的产品和厂商能够支持这一全新的开放标准。就目前来说,能够同时支持H.264和M-JPEG的网络视频产品具有最高的灵活性和集成能力,因此是用户的理想选择。


    原文链接:http://www.axis.com/files/whitepaper/wp_h264_34203_cn_0901_lo.pdf

    更详细的H.264标准可到ITU官网下载:http://www.itu.int/ITU-T/recommendations/rec.aspx?rec=11466&lang=zh
    展开全文
  • VC6.0下的h264编码以及解码
  • 使用tiny4412开发板,USB摄像头采集YUYV422视频数据,经过硬件转码为YUV420(NV12)格式数据,最后通过硬件H264压缩H264数据。程序运行生成NV12格式视频数据文件和H264压缩文件。通过YUVplay 和VLC播放器可进行...
  • 修改hi3531、3531d的例子压缩yuv到h264,频率无法修改,效率很慢,通过修改可以实现了1024*1024的yuv的压缩,以及帧率的设置100hz等
  • h264文件(1080p、3840x2160、480p),压缩文档中包含了以上三个分辨率的264视频
  • H264_test_demo.264

    2020-02-29 16:07:44
    本视频为H264编码的裸流视频,H264是目前比较流行的视频编码技术,常见的经过H264编码后的视频存储为mp4格式,但是不一定mp4格式的视频都是H264压缩的也有H265压缩的,MEPG压缩的,本视频为经过H264压缩的裸流的格式,...
  • 视频和视频帧:H264编码格式整理

    千次阅读 2020-12-14 15:30:59
    主要介绍VCL和NAL,前者与视频编码数据紧密相关,后者和H264格式相关,也是本文介绍的重点。 NAL。介绍NAL的组成单元:NALU。包括NALU的组成结构、类型、几个特殊的NALU,AU等。 H264其他概念。如H264的两种格式:...
  • h264压缩方法

    千次阅读 2017-03-10 15:57:21
    1.分组:把几帧图像分为一组(GOP,也就是一个序列),为防止运动变化,帧数不宜取多。 2.定义帧:将每组内各帧图像定义为三种类型,即I帧、B帧和P帧;... 帧内(Intraframe)压缩也称为空间压缩(Spatial compression)
  • 功能简介: ... 3.yuv420p通过x264编码压缩h264码流 4.将h264码流通过tcp传输到显示端 5.显示端接收h264码流,并保存为本地文件,程序运行结束后可通过VLC播放器直接打开xxxx.h264文件进行播放。
  • https://blog.csdn.net/lxc1014/article/details/45666281
  • H.264是一种视频压缩标准,其只规定了符合标准的码流的格式,以及码流中各个语法元素的解析方法。 H.264标准协议是每个从事多媒体工作的码农必备的参考书,欢迎下载。
  • 富士通微电子美国公司推出一款新的编码格式转换器MB86H52,可将全高清(Full HD)(1920×1080)的MPEG-2视频数据压缩H.264格式,用于高容量消费类产品应用。 新的Fujitsu MB86H52可以将MPEG-2视频数据格式压缩到...
  • yuv420视频转码h264格式

    热门讨论 2013-07-24 12:28:04
    yuv420视频转码h264格式,具体说明,请看压缩包自带的readme。在linux下编译运行。

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 19,577
精华内容 7,830
关键字:

压缩格式h264