> 如何查看视频的IPB帧 Android, 如何获取视频的每一帧的信息
视频 = 图片、图像(摄像头) + 声音(麦克风) ;
谷歌官方给我们的提供的api接口类:MediaMetadataRetriever,这个类是提供给我们用来获取视频信息的
基于ffmpeg实现的,同样的实验获取视频帧信息的速度在1秒左右- https://github.com/wseemann/FFmpegMediaMetadataRetriever
- H.264 IPB帧- https://blog.csdn.net/samuelandkevin/article/details/87975133
一般平均来说,I的压缩率是7(跟JPG差不多),P是20,B可以达到50,可见使用B帧能节省大量空间,节省出来的空间可以用来保存多一些I帧,这样在相同码率下,可以提供更好的画质。
GOP (Group of Pictures)长度为13,S0~S7 表示 8个视点,T0~T12 为 GOP的 13个时刻。每个 GOP包含帧数为视点数 GOP 长度的乘积。在该图中一个 GOP 中,包含94 个 B帧。B 帧占一个 GOP 总帧数的 90.38%。GOP 越长,B 帧所占比例更高,编码的率失真性能越高。
-- live555+ffmpeg如何提取关键帧(I帧,P帧,B帧)
判断I帧,P,B帧方法:
(1):假如解码成功,则不是I帧就是P帧(根据AVFrame->keyframe判断是否是I帧)。
假如不是I帧,也不是P帧,则只能是B帧(通过pts判断)。
(2):采用AVFrame->pict_type综合pts的办法:
if(FF_I_TYPE == picture->pict_type) {
Printlog("
} else if(FF_P_TYPE==picture->pict_type) {
Printlog("
} else if(FF_B_TYPE==picture->pict_type) {
Printlog("
} else if(FF_S_TYPE==picture->pict_type) {
Printlog("
} else {
Printlog("
}
正常情况下是不会打印出B帧的,因为解码成功的肯定是I帧或者是P帧.
根据帧头信息可以读一个个的nal,可能是00 00 01也可能是由四个字节指定的大小。必须要解析,可能读取的nal是pps,sps,或只是一帧的一个slice
解码:是一个协议分析过程,每一帧H264数据都是可以通过观察二进制码流分析出来的。根据协议说明,每一帧图像一般在开头有一个单元分隔符NAL,两个单元分隔符之间的数据包就是一帧图像。就是00 00 01 09,这个09就是单元分隔符的标志。不过协议并没有说NAL流必须如此组织,可能还有其它的组织形式。
一帧可能有几个SLICE的!你要把所有的SLICE定位出来,然后再找到每个SLICE的起始宏块的地址,地址为0的话就是一帧开始了!"
的确,除去sps和pps,单纯从0x0000000101和0x0000000105来看,直接是看不出帧的边界的,只是一个个的slice的边界罢了,但是一个slice的起始宏块的地址是0,则代表该slice是一帧的第一个slice,故也就是一帧的开始了。
当时求帧边界时,用了个取巧的办法:就是当遇到0x0000000101和0x0000000105时,求出对应的dts,而如果帧率为25,则每帧的播放时间是0.04秒,则求出下一个0x0000000101和0x0000000105时,求出其dts,如果和前面的基准dts的差是0.04秒,则认为就是一帧了。ES流中没有dts呀。
【学习ffmpeg】打开视频文件,帧分析,并bmp保存关键帧- https://www.cnblogs.com/jukan/p/7045296.html
ffmpeg如何提取视频的关键帧?-http://www.dewen.net.cn/q/725
enum MEDIA_PACK_TYPE {
FILE_HEAD = 0, // 文件头
VIDEO_I_FRAME = 1, // 视频I帧
VIDEO_B_FRAME = 2, // 视频B帧
VIDEO_P_FRAME = 3, // 视频P帧
VIDEO_BP_FRAME = 4, // 视频BP帧
VIDEO_BBP_FRAME = 5, // 视频B帧B帧P帧
VIDEO_J_FRAME = 6, // 图片帧
AUDIO_PACKET = 10, // 音频包
};
> android解码判断H264的I帧
android解码的时候,从TCP的socket处获取到了流,再把流送到解码器去解码,中间有个过程就是要判断一下流中的I帧,不然播放出来就是花屏了,如何判断I帧
public class CheckIFrame {
public static boolean isIFrame(byte[] data) {
if( data &#61;&#61; null || data.length <5) {
return false;
}
Log.i("IFrame", "data0:"&#43;toHex(data[0])&#43;"--data[1]:"&#43;toHex(data[1])&#43;"--data[2]:"&#43;toHex(data[2])&#43;
"--data3:"&#43;toHex(data[3])&#43;"--data4:"&#43;toHex(data[4]));
if (data[0] &#61;&#61; 0x0
&& data[1] &#61;&#61; 0x0
&& data[2] &#61;&#61; 0x0
&& data[3] &#61;&#61; 0x1
&& data[4] &#61;&#61; 0x67) {
Log.d("IFrame", "check I frame data: " &#43; Arrays.toString(Arrays.copyOf(data, 5)));
return true;
}
byte nalu &#61; data[4];
return ((nalu & 0x1F) &#61;&#61; 5) ? true : false;
}
public static String toHex(byte b) {
String result &#61; Integer.toHexString(b & 0xFF);
if (result.length() &#61;&#61; 1) {
result &#61; &#39;0&#39; &#43; result;
}
return result;
}
}
> IPB帧编码顺序
IPB帧编码顺序&#xff08;解码顺序&#xff09;与显示顺序- https://www.cnblogs.com/yinxiangpei/articles/2828118.html
仅仅使用前一个显示的基准帧来编码的帧被称为P帧&#xff0c;同时使用前一个显示帧和未来帧作为基准帧进行编码的帧称为B帧.
在通常的场景中&#xff0c;编解码器编码一个I帧&#xff0c;然后向前跳过几个帧&#xff0c;用编码I帧作为基准帧对一个未来P帧进行编码&#xff0c;然后跳回到I帧之后的下一个帧。
编码的I帧和P帧之间的帧被编码为B帧。之后&#xff0c;编码器会再次跳过几个帧&#xff0c;使用第一个P帧作为基准帧编码另外一个P帧&#xff0c;然后再次跳回&#xff0c;用B帧填充显示序列中的空隙。 这个过程不断继续&#xff0c;每12到15个P帧和B帧内插入一个新的I帧。
通常&#xff0c;更换场景后的第一帧就是I帧&#xff0c;I帧应当全帧传送。从压缩的程度来看&#xff0c;I画面的压缩量最少&#xff1b;P画面次之&#xff0c;它是以I画面为基础&#xff1b;B画面压缩最多。为了加大压缩比&#xff0c;通常在I帧后面相隔2帧&#xff08;最多3帧&#xff09;设置1个P帧&#xff0c;在I、P帧之间都是B帧&#xff0c;在两个P帧之间也是设置2&#xff5e;3帧B帧。B帧传送它与I帧或P帧之间的差值信息&#xff0c;或者P帧与后面P帧或I帧之间的差值信息&#xff0c;或者它与前后I、P帧或P、P帧平均值之间的差值信息。当主体内容变化愈大时&#xff0c;两个I画面之间的帧数值越小&#xff1b;当主体内容变化小时&#xff0c;I面画的间隔可以适当大一些。或者说&#xff0c;B帧、P帧所占比例越大&#xff0c;图像压缩比越高。一般两个I画面相隔13&#xff5e;15帧&#xff0c;相隔帧数不宜再多。
下面以15帧为例&#xff0c;说明VCD图像帧的排列顺序。I、P、B三种画面的典型设置方式&#xff0c;对NTSC制共约需半秒时间。节目输入顺序是按实际出现顺序排列的&#xff0c;即I、B、B、P、B、B、P、B、B、I、B、B、P&#xff1b;但为了解码时便于从I、P画面插补得到B画面&#xff0c;在编码录制节目时&#xff0c;将顺序改变了&#xff0c;即按照I、P、B、B顺序&#xff0c;即改为按原来0、3、1、2、6、4、5、9、7、8的画面顺序。解码时先解出0帧、3帧&#xff0c;再由其插补预测计算得出1帧、2帧等等。为此&#xff0c;须在解码器内设置动态存储器&#xff0c;将I、P帧先解码并存储&#xff0c;再计算出各个B帧。不过最后输出时&#xff0c;还是应当按照实际播放顺序重组读出&#xff0c;按正确顺序输出。
IPB帧编码顺序&#xff08;解码顺序&#xff09;与显示顺序- https://blog.csdn.net/awangqm/article/details/51077842
音视频编码卡的视频编码算法从JPEG 发展到MPEG-1、MPEG-2、MPEG-4和H.264。JPEG是一种著名的图像压缩方法&#xff0c;最初由Joint Photographic Experts Group在1986年提出并于1992年正式成为ISO标准(ISO/IEC 10918),主要应用于静态图像压缩&#xff0c;如果把它用在运动图像压缩的时候&#xff0c;就是我们通常所说的Motion-JPEG&#xff0c;由于JPEG相当于MPEG的帧内压缩&#xff0c;因而没有去除时域上的冗余&#xff0c;所以在保证一定图像质量的时候&#xff0c;压缩比不高&#xff0c;通常只有10-30倍&#xff0c;但是它有一个固定的优点&#xff0c;就是延迟在40ms&#xff0c;实时性很好&#xff0c;所以在某些特殊应用的场合仍然可以看到它的踪影。MPEG运动图像编码技术标准是由Motion Picture Experts Group在1988年提出&#xff0c;并于1992年11月通过&#xff0c;1993年8月作为ISO/IEC 11172标准公布&#xff0c;这就是通常所说的MPEG-1。MPEG-1为了追求更高的压缩效率&#xff0c;更注重去除图像系列的时间冗余度。因此引入了I帧(帧内编码)、P帧(前向预测编码)、B帧(双向预测编码)。P帧由前一个I帧或P帧图像来预测&#xff0c;而B帧由前后的两个P帧或一个I帧和一个P帧来预测&#xff0c;因而编解码和帧的显示顺序有所不同。
数字视频处理中的IPB帧编码和GOP图像组- https://blog.csdn.net/KID_yuan/article/details/84947231
> android 如何获取网络视频的第一帧
如何获取网络视频&#xff08;或者说后台返回视频URL&#xff09;第一帧图片以及获取本地视频的第一帧图片- https://blog.csdn.net/qq_23418393/article/details/65441631
String pathvideo &#61; "你的网络视频路径";
//加载视频第一帧
Bitmap bitmap &#61; getNetVideoBitmap(pathvideo);
holder.img.setImageBitmap(bitmap);//对应的ImageView赋值图片
public static Bitmap getNetVideoBitmap(String videoUrl) {
Bitmap bitmap &#61; null;
MediaMetadataRetriever retriever &#61; new MediaMetadataRetriever();
try {
//根据url获取缩略图
retriever.setDataSource(videoUrl, new HashMap());
//获得第一帧图片
bitmap &#61; retriever.getFrameAtTime();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} finally {
retriever.release();
}
return bitmap;
}
安卓手机上&#xff0c;哪一个软件可以查看视频的详细信息&#xff0c;包括分辨率&#xff0c;码率&#xff0c;比特率&#xff0c;帧数&#xff1a;vplay软件&#xff1b;mxplayer
> Android获取摄像头视频帧并实时处理&#xff0c;预览视频
Android有一种机制&#xff0c;如果想在回调函数onPreviewFrame(byte[] data, Camera camera)中获取视频数据&#xff0c;必须调用camera.startPreview(); onPreviewFrame才会有视频数据回调过来。
public class CameraPreview extends Activity implements SurfaceHolder.Callback,PreviewCallback{
SurfaceHolder surfaceHolder ;
Camera camera ;
&#64;Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
SurfaceView view &#61; (SurfaceView) findViewById(R.id.surface_view);
view.getHolder().addCallback(this);
view.getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}
public void surfaceCreated(SurfaceHolder holder) {
}
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
try{
camera &#61; Camera.open();
camera.setPreviewDisplay(holder);
Parameters params &#61; camera.getParameters();
params.setPreviewSize(352, 288);
camera.setParameters(params);
camera.startPreview() ;
camera.setPreviewCallback(this);
}catch(Exception e){
e.printStackTrace();
}
}
public void surfaceDestroyed(SurfaceHolder holder) {
if(camera !&#61; null) camera.release() ;
camera &#61; null ;
}
public void onPreviewFrame(byte[] data, Camera camera) {
Log.i("jefry", "vedio data come ...");
}
}