mp4视频文件截图--h264解码成yuv再转存为bmp图片
得到yuv序列
从一个mp4文件中取出一张截图,这个直接用ffmpeg命令行就可以完成,这里想分析下原理,大致的流程原理如标题,将mp4文件解复用得到一个视频流,这里就以h264编码的文件为例,当然实际的视频流不一定是h264,可能是mpeg4 、hevc(h265)、vp8 vp9 av1等等。 和一个音频流,aac mp3等。 现在这里只讨论视频流, 将这个h264视频流解码,解码为yuv, 然后将yuv序列中指定的一帧图片内容转换为rgb,再存储为bmp位图。
前面的步骤,解复用,解码,直接用ffmpeg来完成了:
#ffmpeg -i tfdf.mp4 -ss 00:00:10 -t 10 -pix_fmt yuyv422 672x378_yuyv422.yuv
-i tfdf.mp4 输入文件
-ss 00:00:10 从文件的第00时00分10秒开始
-t 取10s时间的长度
-pix_fmt yuyv422 输出格式yuyv422
672x378_yuyv422.yuv 输出文件名
得到yuyv422文件序列,也可以输出yuv420格式
#ffmpeg -i tfdf.mp4 -ss 00:00:10 -t 10 -pix_fmt yuv420p 672x378_yuv420p.yuv
具体哪些输出格式,ffmpeg 源码 /libavutil/pixdesc.c 中结构体av_pix_fmt_descriptors中可以查看
这里来分析下yuv420, yuv421, yuv422, yuyv422的存储方式:
Y 表示亮度信号,即灰阶值。
UV 色度 和 饱和度。两者一般一起成对表示。也有的叫YCrCb,统一都可叫做 YUV .
这里是基于模拟信号来表示其含义的,所谓420,即N1:N2:N3 ,
N1表示Y奇数行加上偶数行Y样本的个数。 为4
N2 表示奇数行 Cr加上Cb样本的个数(这两个成对,才有意义),为2
N3 表示偶数行 Cr加上Cb样本的个数 为0.
所以就是YUV420。 每四个像素,共用一个uv分量。而Y分量是一个像素对应一个,所以这个420相比于422来说,利用的是人眼对于亮度的敏感度比对色度的敏感度要高的原理,适当减小色度的信息量,但是人的视觉对色度信息被阉割之后的视频看起来差别不大,这样可以达到节省带宽和存储空间的目的。
那么yuv420 和yuyv422? 名称上也可以看出来,前者是把一帧画面的y分量先连续存储在一起,然后在分别把u分量和v分量存储,一帧图像分成了三个部分存储,如果我们从文件顺序读取显示的话,看到的应该是先从上到下刷出一个黑白图(y), 然后再加上颜色,要把它转换为RGB, 也是应该读完整个图之后在转换。这种存储方式称为 planar 平面存储。 即yuv420p
yuyv422, 就是交叉的方式存储,Y0 Cb Y1 Cr , Y U Y V ,也称为packed打包格式。
这两种存储方式,很明显交叉存储的方式它的容错能力是要强一些的,即使读到半帧数据,也是可以显示半帧的,但是yuv平面存储的方式是要读完整帧,比如下面的 672x378 yuyv422格式的元数据,播放的时候把它的高度故意设置错误,图像也是可以分辨出来的:
而yuv420p的数据,如果高度设置错误,yuv 分量就都错位了,整个播放的一片浑浊。
取出一个yuv帧来,转为rgb,然后存储为BMP格式图片。
得到yuv帧: 1.0 上述的从视频文件中取yuv序列。
2.0 从 bmp/jpg/png 图片中取
ffmpeg -i jianbian.jpg -pix_fmt yuv420p jianbian.yuv
ffmpeg -i jianbian.bmp -pix_fmt yuv420p jianbian.yuv
ffmpeg -i jianbian.bmp -pix_fmt rgb24 jianbian.rgb
将yuv420p转为rgb:
1.0 利用ffmpeg (ffmpeg /libswscale/yuv2rgb.c 源码对应)
ffmpeg -s 3840x2160 -pix_fmt yuv420p -i jianbian.yuv -pix_fmt rgb24 jianbia_yuv2rgb.rgb
2.0 这里从android7 的源码中,有关于 yuv2rgb的功能,在 https://www.androidos.net.cn/android/7.1.1_r28/xref/frameworks/av/media/libstagefright/yuv/YUVImage.cpp
void YUVImage::yuv2rgb(uint8_t yValue, uint8_t uValue, uint8_t vValue,uint8_t *r, uint8_t *g, uint8_t *b) const {int rTmp = yValue + (1.370705 * (vValue-128));int gTmp = yValue - (0.698001 * (vValue-128)) - (0.337633 * (uValue-128));int bTmp = yValue + (1.732446 * (uValue-128));*r = clamp(rTmp, 0, 255);*g = clamp(gTmp, 0, 255);*b = clamp(bTmp, 0, 255);
}
这里借一张图,说明一下 yuv转换公式中,uv的对应值关系,yuv420p中因为 uv的个数是不够的,所以是每四个y公用一组uv,当然不是在一行的连续4个像素的y值,而是如下4x4块状,在实际图中相邻的四个像素公用一组uv
这里有个疑惑,如果是把原本rgb24的转换成yuv420, 原理上讲应该是有丢失信息的,在把它从yuv420转换回来rgb24, 应该是有所差别的吧。个人按照网上找的给类yuv转rgb的公式,也写了一个demo(方便对照原理看的一个代码):
#include<stdio.h>
#include<stdlib.h>
unsigned char clamp(unsigned char v, unsigned char min, unsigned char max)
{if(max<=min){printf("erro");return 0;}if(v>max) return max;else if(v<min) return min;else return v;
}
int main(int argc, const char*argv[])
{
#define WIDTH 3840
#define HEIGHT 2160if(argc<2){printf("usage:yuv420p_file\n");return 0;}FILE *fp_in = fopen(argv[1],"r");if(fp_in == NULL){printf("fopen erro:%s\n",argv[1]);return -1;}FILE *fp_out = fopen("out.rgb","w+");if(fp_out == NULL){printf("fopen out.rgb erro");fclose(fp_in);return -1;}unsigned char *buffer_in = malloc((WIDTH*HEIGHT*1.5));unsigned char *buffer_out = malloc(WIDTH*HEIGHT*3);unsigned char y[4]={0};unsigned char u=0;unsigned char v=0;unsigned char r[4]={0};unsigned char g[4]={0};unsigned char b[4]={0};fread(buffer_in,1,WIDTH*HEIGHT*1.5,fp_in);int u_base_offset = WIDTH*HEIGHT;int v_base_offset = u_base_offset+WIDTH*HEIGHT/4;int i =0;int j =0;for(i=0;i<HEIGHT;i+=2){for(j=0;j<WIDTH;j+=2){//每次处理 相邻 4x4块 4个像素y[0]=buffer_in[i*WIDTH+j];y[1]=buffer_in[i*WIDTH+j+1];y[2]=buffer_in[(i+1)*WIDTH+j];y[3]=buffer_in[(i+1)*WIDTH+j+1];u=buffer_in[u_base_offset+j/2+i/2*WIDTH/2];v=buffer_in[v_base_offset+j/2+i/2*WIDTH/2];int k =0;for(k=0;k<4;k++){double rd=0,gd=0,bd=0;// rd = 1.164*(y[k]-16)+1.596*(v-128);// gd = 1.164*(y[k]-16)-0.813*(u-128) - 0.392*(v-128);// bd = 1.164*(y[k]-16)+2.017*(u-128);rd = y[k] + (1.370705*(v-128));gd = y[k] - (0.698001*(v-128)) - (0.337633*(v-128));bd = y[k] + (1.732446*(u-128));r[k] = clamp((unsigned char)rd,0,255);g[k] = clamp((unsigned char)gd,0,255);b[k] = clamp((unsigned char)bd,0,255);}char *dest_pix_ptr[4] = {buffer_out+(i*WIDTH+j)*3, buffer_out+(i*WIDTH+j+1)*3, buffer_out+((i+1)*WIDTH+j)*3,buffer_out+((i+1)*WIDTH+j+1)*3};//给4个像素点赋值for(k=0;k<sizeof(dest_pix_ptr)/sizeof(dest_pix_ptr[0]);k++){*(dest_pix_ptr[k]) = r[k];*(dest_pix_ptr[k]+1) = g[k];*(dest_pix_ptr[k]+2)= b[k];}}}fwrite(buffer_out,1,WIDTH*HEIGHT*3,fp_out);free(buffer_in);free(buffer_out);fclose(fp_out);fclose(fp_in);
}
在虚拟机 ubuntu上转换得到的图像某些像素就有问题:左为利用ffmpeg命令将yuv转rgb24的输出,右为上述公式计算得到的输出。?????究竟是浮点计算的误差?还是另有蹊跷?
将rgb24存储为bmp格式
这里对bmp格式不做太多的解析,对于rgb24,即每一个像素点用三个字节 分别存储 r g b三个值,就是24bit,颜色深度为24bit, 简单的存储方式,就是存储一个bmp文件格式的头,然后存储 rgb raw数据就可以了,(对于不是24bit的存储方式还有调色板什么的,这里不去掺和了,简单起见。)
比如 我们在photoshop中创建一个4x4像素的图片,如下:
每一个像素给它一个不同的颜色值,存储为bmp位图的话,bmp文件源数据是这样的:
bmp文件格式,主要四部分组成:
文件头,信息头,调色板,数据。 这里rgb24的话,就不需要调色板了。
借博客一用,bitmap格式讲的很清楚:https://blog.csdn.net/u013066730/article/details/82625158
mp4视频文件截图--h264解码成yuv再转存为bmp图片相关推荐
- 如何将MP4视频文件转换成MP3音频格式
2018年11月9日,美国漫威影业公司的大作<毒液:致命守护者>开始在中国上映,作为漫威在2018年最后的压轴巨作自然是非常不错的.在影片中很多激斗的场景也有共生体和宿主之间的对话,但是电 ...
- 如何将MP4视频文件转换成GIF动态图片
在观看电视剧时,剧中常会出现很多搞笑滑稽的镜头,比如把视频MP4转GIF动态图片,那么效果就很好了.虽说现在很多的手机视频播放器都有制作GIF的功能,不过也就10秒钟左右,时间很短,如果使用软件制作G ...
- Mp4 分割 怎么将mp4视频文件分割成几段
当我们需要分割视频或音频的某一段时,就需要通过分割视频来实现.虽然分割视频是一件很容易的事,但对于初学者来说,面对网上五花八门的号称能分割视频的软件却又要花费一段时间去摸索,实在耗时耗力.今天小编也根 ...
- java 视频 合并成一个_Java 合并多个MP4视频文件
局限性 只支持MP4文件 经过尝试对于一些MP4文件分割不了 依赖 com.googlecode.mp4parser isoparser 1.1.22 工具类 package com.example. ...
- atitit.mp4 视频文件多媒体格式结构详解
atitit.mp4 视频文件多媒体格式结构详解 1. 一.基本概念1 2. MP4文件概述2 3. mp4是由一个个"box"组成的,2 4. 典型简化mp43 5. Fragm ...
- 怎么修复SONY索尼相机摄像机断电死机损坏的MP4视频文件
索尼SONY相机a系列微单,摄像机AX,NX系统等录制的MP4文件,拍摄中如果遇到相机死机,断电,电池没电等异常情况,一般在相机存储卡里或者相机内存里会有文件,但是这个文件相机无法回放,导到电脑中也播 ...
- GIF转MP4 - 在线将GIF动态图转为MP4视频文件
如何把 GIF 转成视频?一刀工具箱提供几秒钟内将 GIF 转换为 MP4 文件的最佳方法,在线将 GIF 动态图转为 MP4 视频文件,100% 免费.安全并便于操作! 代码片段 chooseIma ...
- 佳能相机MP4视频文件变小或打不开播放不了怎么修复
佳能MP4视频文件损坏打不开播放不了 遇到这样一个问题,视频是用佳能5D4录制的MP4格式视频素材 ,视频拍摄一切正常,拒回忆在相机上回放也没有任何问题,但是回来用读卡器导素材时却发现有一半素材是坏的 ...
- java实现MinIO文件上传,并将视频文件截图,将视频封面及视频通过MinIo上传到服务器中
java实现MinIO文件上传,并将视频文件截图,将视频封面及视频通过MinIo上传到服务器中 配置完毕,接下来开始代码编写. 说明 总过程分为两步. 1.配置MinIO的环境. 2.代码编写. 下面 ...
- 常用MIME类型,解决IIS布署后字体文件、mp4视频文件等not found 的错误
前言:项目在本地运行正常,但部署在IIS服务器后使用浏览器访问项目会报:404 not found 错误,包括mp4视频文件和woff文件找不到..如下 原因:在IIS中没有将 .mp4和 .woff ...
最新文章
- python bytearray拼接_python数据类型 ——bytes 和 bytearray
- ubuntu 命令卡住_如何在Ubuntu系统中重置root密码
- 护卫神怎么增加php版本_护卫神php套件 php版本升级方法(php5.5.24)
- C#结合Jquery LigerUI Tree插件构造树
- android之WIFI小车编程详述
- 如何写一个pyton模块
- 百度地图上的标注物太多导致界面卡顿的解决办法
- 知乎技术热帖:Qt 这么强大为什么火不起来?
- 如何设置内网和外网同时使用
- Flutter 未检测到iOS模拟器以及Android Studio无法获取iOS模拟器的问题
- nextjs的发布,pm2发布nextjs项目
- 剖析Android shape标签的绘制
- RESTful Web 服务:教程
- linux stm32 swd,stm32 JTAG和SWD的使用区别
- 最新php版本下载地址
- 黑马程序员——Java语言基础——关键字、表示符、常量、变量及数据的类型、运算符
- AndroidStudio恢复误删文件
- 匿名内部类会导致内存泄露
- Vim - 官方网站
- 洛谷 P1240 诸侯安置
热门文章
- mysql端口被占用了如何解决_如何解决Win10安装MYSQL端口被占用?
- SMILES的基本规则
- hilbert方程组matlab,数值分析(Hilbert矩阵)病态线性方程组的求解Matlab程序
- 换个角度理解Android的AIDL原理
- 计算机软件需求说明编制指南gb/t 9385-2008,GBT 9385-2008 计算机软件需求说明编制指南.pdf...
- 基于AT89S52单片机的汽车LED尾灯控制器设计
- 计算机网络网线制作工具有,网线制作工具 网线水晶头制作过程详解(视频+图文教程)...
- 基于MATLAB的数字信号处理(2) 时域采样和频域采样
- 交通灯—VHDL设计
- db9针232接口波特率标准_RS232 DB9 计算机接口定义