查看视频的IPB帧,IPB帧编码顺序,视频的第一帧图片

> 如何查看视频的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("<II>");
 } else if(FF_P_TYPE==picture->pict_type) {
  Printlog("<PP>");
 } else if(FF_B_TYPE==picture->pict_type) {
  Printlog("<BB>");
 } else if(FF_S_TYPE==picture->pict_type) {
  Printlog("<SS>");
 } else {
  Printlog("<OtherType>");
 }
正常情况下是不会打印出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 == null || data.length < 5) {
    return false;
    }
    Log.i("IFrame", "data0:"+toHex(data[0])+"--data[1]:"+toHex(data[1])+"--data[2]:"+toHex(data[2])+
    "--data3:"+toHex(data[3])+"--data4:"+toHex(data[4]));
    if (data[0] == 0x0
    && data[1] == 0x0
    && data[2] == 0x0
    && data[3] == 0x1
    && data[4] == 0x67) {
    Log.d("IFrame", "check I frame data: " + Arrays.toString(Arrays.copyOf(data, 5)));
    return true;
    }
    byte nalu = data[4];
    return ((nalu & 0x1F) == 5) ? true : false;
    }
    
    public static String toHex(byte b) {  
        String result = Integer.toHexString(b & 0xFF);  
        if (result.length() == 1) {  
            result = '0' + result;  
        }  
        return result;  
    }  
}

> IPB帧编码顺序
IPB帧编码顺序(解码顺序)与显示顺序- https://www.cnblogs.com/yinxiangpei/articles/2828118.html
  仅仅使用前一个显示的基准帧来编码的帧被称为P帧,同时使用前一个显示帧和未来帧作为基准帧进行编码的帧称为B帧.
在通常的场景中,编解码器编码一个I帧,然后向前跳过几个帧,用编码I帧作为基准帧对一个未来P帧进行编码,然后跳回到I帧之后的下一个帧。
  编码的I帧和P帧之间的帧被编码为B帧。之后,编码器会再次跳过几个帧,使用第一个P帧作为基准帧编码另外一个P帧,然后再次跳回,用B帧填充显示序列中的空隙。 这个过程不断继续,每12到15个P帧和B帧内插入一个新的I帧。
  通常,更换场景后的第一帧就是I帧,I帧应当全帧传送。从压缩的程度来看,I画面的压缩量最少;P画面次之,它是以I画面为基础;B画面压缩最多。为了加大压缩比,通常在I帧后面相隔2帧(最多3帧)设置1个P帧,在I、P帧之间都是B帧,在两个P帧之间也是设置2~3帧B帧。B帧传送它与I帧或P帧之间的差值信息,或者P帧与后面P帧或I帧之间的差值信息,或者它与前后I、P帧或P、P帧平均值之间的差值信息。当主体内容变化愈大时,两个I画面之间的帧数值越小;当主体内容变化小时,I面画的间隔可以适当大一些。或者说,B帧、P帧所占比例越大,图像压缩比越高。一般两个I画面相隔13~15帧,相隔帧数不宜再多。
  下面以15帧为例,说明VCD图像帧的排列顺序。I、P、B三种画面的典型设置方式,对NTSC制共约需半秒时间。节目输入顺序是按实际出现顺序排列的,即I、B、B、P、B、B、P、B、B、I、B、B、P;但为了解码时便于从I、P画面插补得到B画面,在编码录制节目时,将顺序改变了,即按照I、P、B、B顺序,即改为按原来0、3、1、2、6、4、5、9、7、8的画面顺序。解码时先解出0帧、3帧,再由其插补预测计算得出1帧、2帧等等。为此,须在解码器内设置动态存储器,将I、P帧先解码并存储,再计算出各个B帧。不过最后输出时,还是应当按照实际播放顺序重组读出,按正确顺序输出。

IPB帧编码顺序(解码顺序)与显示顺序- https://blog.csdn.net/awangqm/article/details/51077842
  音视频编码卡的视频编码算法从JPEG 发展到MPEG-1、MPEG-2、MPEG-4和H.264。JPEG是一种著名的图像压缩方法,最初由Joint Photographic Experts Group在1986年提出并于1992年正式成为ISO标准(ISO/IEC 10918),主要应用于静态图像压缩,如果把它用在运动图像压缩的时候,就是我们通常所说的Motion-JPEG,由于JPEG相当于MPEG的帧内压缩,因而没有去除时域上的冗余,所以在保证一定图像质量的时候,压缩比不高,通常只有10-30倍,但是它有一个固定的优点,就是延迟在40ms,实时性很好,所以在某些特殊应用的场合仍然可以看到它的踪影。MPEG运动图像编码技术标准是由Motion Picture Experts Group在1988年提出,并于1992年11月通过,1993年8月作为ISO/IEC 11172标准公布,这就是通常所说的MPEG-1。MPEG-1为了追求更高的压缩效率,更注重去除图像系列的时间冗余度。因此引入了I帧(帧内编码)、P帧(前向预测编码)、B帧(双向预测编码)。P帧由前一个I帧或P帧图像来预测,而B帧由前后的两个P帧或一个I帧和一个P帧来预测,因而编解码和帧的显示顺序有所不同。

数字视频处理中的IPB帧编码和GOP图像组- https://blog.csdn.net/KID_yuan/article/details/84947231

> android 如何获取网络视频的第一帧
如何获取网络视频(或者说后台返回视频URL)第一帧图片以及获取本地视频的第一帧图片- https://blog.csdn.net/qq_23418393/article/details/65441631
String pathvideo = "你的网络视频路径";
//加载视频第一帧
Bitmap bitmap = getNetVideoBitmap(pathvideo);
holder.img.setImageBitmap(bitmap);//对应的ImageView赋值图片

public static Bitmap getNetVideoBitmap(String videoUrl) {
    Bitmap bitmap = null;
    MediaMetadataRetriever retriever = new MediaMetadataRetriever();
    try {
        //根据url获取缩略图
        retriever.setDataSource(videoUrl, new HashMap());
        //获得第一帧图片
        bitmap = retriever.getFrameAtTime();
    } catch (IllegalArgumentException e) {
        e.printStackTrace();
    } finally {
        retriever.release();
    }
    return bitmap;
}

安卓手机上,哪一个软件可以查看视频的详细信息,包括分辨率,码率,比特率,帧数:vplay软件;mxplayer

> Android获取摄像头视频帧并实时处理,预览视频
  Android有一种机制,如果想在回调函数onPreviewFrame(byte[] data, Camera camera)中获取视频数据,必须调用camera.startPreview(); onPreviewFrame才会有视频数据回调过来。
public class CameraPreview extends Activity implements SurfaceHolder.Callback,PreviewCallback{       
    SurfaceHolder surfaceHolder ;    
    Camera camera ;  
  
    @Override  
    public void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.main);  
        SurfaceView view = (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 = Camera.open();  
            camera.setPreviewDisplay(holder);  
            Parameters params = 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 != null) camera.release() ;  
        camera = null ;  
    }  
      
    public void onPreviewFrame(byte[] data, Camera camera) {  
       Log.i("jefry", "vedio data come ...");  
          
    }       

猜你喜欢

转载自blog.csdn.net/ShareUs/article/details/89215563