关于iOS硬解码遇到多slice的视频码流

VideoToolBox兼容不标准的多slice视频 --转自:网易云信

在iOS平台的硬解的实践中,我们可能会遇到如下图的这种情况(上面一部分有画面,下面部分是绿屏):

这种现象实际上就是多slice视频的组织格式不符合VideoToolBox的要求引起的。一般出现在某些华为手机,小米手机,编码大分辨率视频流的时候,进行分片编码。

以上图的视频为例,该视频流的每一帧是由3个slice构成的,对于VideoToolBox可以正常解码的组织格式应该如下图所示:

而该视频的帧组织方式则如下图所示:

可以看出,该视频混用了AVCC与Annex-B格式的分隔符,导致iOS VideoToolBox只能解码第一个slice单元,从而出现下半部分绿屏的情况。

  • 对于这类问题视频的处理: 如果是源视频流可控,可以调整源视频流的打包方式,按第一种图示的方式打包。
  • 对于不可控的场景,播放器也可以做下兼容:因为一个NALU中的内容一定是不包含startcode的,所以如果在一个NALU中找到了startcode,就可以将其处理成第一种图示中的格式。

修改方式寻找一帧中的起始码,通过计算字节差,计算每个片的size,将片段中的00000001替换成新计算的size(每片对应自己的size)

以下边简单地两片为例:

unsigned char* bufferOneFream,   unsigned int buffFreanLen,    h264的数据和长度

-------------------------------------------------------------------


int naluType = 0;                                                               

    unsigned int oneFreamIndex = 0;

    unsigned int spsIndex = 0;

    unsigned int ppsIndex = 0;

    unsigned char* spsPtr = NULL;

    unsigned char* ppsPtr = NULL;

    unsigned char* framePtr = NULL;

    unsigned int contentLen = 0;                                                   //有效码流长度

    unsigned char imgLenArr[4]={0};                                                //起使码转换成长度子节

    static unsigned int spsLen ;                                                      //sps字段长度

    static unsigned int ppsLen ;                                                      //pps字段长度

    static unsigned char* spsBuff;                                                    //sps字段缓存

    static unsigned char* ppsBuff;

    unsigned char* contentFream;

    bool  multislice;

    CVPixelBufferRef pixelbuffer = NULL;                                          //该帧数据,硬解的结果

while(oneFreamIndex < (buffFrameLen))                                       //start code
    {
        multislice = false;//默认非多slice
        if(bufferOneFream[oneFreamIndex] == 0x00
           && bufferOneFream[oneFreamIndex + 1] == 0x00
           && bufferOneFream[oneFreamIndex + 2] == 0x00
           && bufferOneFream[oneFreamIndex + 3] == 0x01)
        {
            naluType = (bufferOneFream[oneFreamIndex + 4]) & 0x0F;
            switch(naluType)
            {
                case 0x06:// SEI
                    break;

                case 0x09://aud
                    //std::cout<<"aud"<<std::endl;
                    break;

                case 0x07:                                                                  //sps
                    //std::cout<<"sps"<<std::endl;
                    spsPtr = &bufferOneFream[oneFreamIndex + 4];
                    spsIndex = oneFreamIndex + 4;
                    break;

                case 0x08:
                    //std::cout<<"pps"<<std::endl;                                                    //pps
                    ppsPtr = &bufferOneFream[oneFreamIndex + 4];
                    ppsIndex = oneFreamIndex + 4;

                    spsBuff=(unsigned char*)malloc(ppsIndex-spsIndex-4);
                    memcpy(spsBuff,spsPtr,(ppsIndex-spsIndex-4));                          //memcpy sps
                    spsLen = ppsIndex-spsIndex-4;

                    break;

                case 0x05://i frame
                    //std::cout<<"i fream"<<std::endl;
                {
                    ppsBuff=(unsigned char*)malloc(oneFreamIndex - ppsIndex);
                    memcpy(ppsBuff,ppsPtr,(oneFreamIndex - ppsIndex));                     //memcpy pps
                    ppsLen = oneFreamIndex - ppsIndex;                                     //memcpy PPS
                    ////I帧内容
                    framePtr =  &bufferOneFream[oneFreamIndex + 4];                         //内容流的位置
                    contentLen = buffFreanLen - (oneFreamIndex + 4);                        //内容流的长度,不包含4字节start code

                    int nalu_size1 =0;
                    int nale_size2 =0;
                    int nextStartcode = 0;
                    for(int index2 = 0; index2< contentLen;index2++)
                    {
                        if(bufferOneFream[oneFreamIndex + 4+ index2]==0x00
                           && bufferOneFream[oneFreamIndex + 5+ index2] == 0x00
                           && bufferOneFream[oneFreamIndex + 6+ index2] == 0x00
                           && bufferOneFream[oneFreamIndex + 7+ index2] == 0x01)////找第二个nale 的start code
                        {
                            nextStartcode = index2;
                            multislice = true;
                            break;
                        }

                    }
                    if(multislice == true)
                    {
                        NSLog(@"i yes");
                        nalu_size1 = nextStartcode;
                        nale_size2 = contentLen - nextStartcode - 4;///slice2的内容要减去前边的四个字节

                        bufferOneFream[oneFreamIndex + 4+ nalu_size1]=(nale_size2 & 0xff000000)>>24;
                        bufferOneFream[oneFreamIndex + 5+ nalu_size1] = (nale_size2 & 0x00ff0000)>>16;
                        bufferOneFream[oneFreamIndex + 6+ nalu_size1] = (nale_size2 & 0x0000ff00)>>8;
                        bufferOneFream[oneFreamIndex + 7+ nalu_size1] = (nale_size2 & 0x000000ff);
                        ////////
                        imgLenArr[0] = (nalu_size1  & 0xff000000)>>24;                           //大端码流长度转换
                        imgLenArr[1] = (nalu_size1  & 0x00ff0000)>>16;
                        imgLenArr[2] = (nalu_size1  & 0x0000ff00)>>8;
                        imgLenArr[3] = nalu_size1  & 0x000000ff;
                        //                    imgLenArr[0] = (contentLen & 0xff000000)>>24;                           //大端码流长度转换
                        //                    imgLenArr[1] = (contentLen & 0x00ff0000)>>16;
                        //                    imgLenArr[2] = (contentLen & 0x0000ff00)>>8;
                        //                    imgLenArr[3] = contentLen & 0x000000ff;
                        //
                        contentFream = (unsigned char*)malloc(contentLen + 4);
                        memcpy(contentFream,imgLenArr, 4);
                        memcpy(contentFream+4 ,framePtr, contentLen);                                           //表示长度的四个字节和码流内容

                        pixelbuffer = HardDecode(contentFream,contentLen+4, spsBuff, spsLen, ppsBuff, ppsLen,1);//解码后数据
                        /////复位
                        multislice = false;
                        //保存yuv数据///////////////////////////////////////////////////////////////////
                        //SaveYUV(pixelbuffer, fout);
                        free(contentFream);
                        return pixelbuffer;

                    }else{////signal slice
                        NSLog(@"i no");
                        imgLenArr[0] = (contentLen & 0xff000000)>>24;                           //大端码流长度转换
                        imgLenArr[1] = (contentLen & 0x00ff0000)>>16;
                        imgLenArr[2] = (contentLen & 0x0000ff00)>>8;
                        imgLenArr[3] = contentLen & 0x000000ff;

                        contentFream = (unsigned char*)malloc(contentLen + 4);
                        memcpy(contentFream,imgLenArr, 4);
                        memcpy(contentFream+4 ,framePtr, contentLen);                                           //表示长度的四个字节和码流内容

                        pixelbuffer = HardDecode(contentFream,contentLen+4, spsBuff, spsLen, ppsBuff, ppsLen,1);//解码后数据
                        //保存yuv数据///////////////////////////////////////////////////////////////////
                        //SaveYUV(pixelbuffer, fout);
                        free(contentFream);
                        return pixelbuffer;
                    }

                }
                case 0x01://p fream
                {

                    //std::cout<<"p fream"<<std::endl;
                    framePtr =  &bufferOneFream[oneFreamIndex + 4];
                    contentLen = buffFreanLen - (oneFreamIndex + 4);
                    int nalu_size1 =0;
                    int nale_size2 =0;
                    int nextStartcode = 0;
                    for(int index2 = 0; index2< contentLen;index2++)
                    {
                        if(bufferOneFream[oneFreamIndex + 4+ index2]==0x00
                           && bufferOneFream[oneFreamIndex + 5+ index2] == 0x00
                           && bufferOneFream[oneFreamIndex + 6+ index2] == 0x00
                           && bufferOneFream[oneFreamIndex + 7+ index2] == 0x01)
                        {
                            nextStartcode = index2;
                            multislice = true;
                            break;
                        }

                    }
                    if(multislice ==  true)
                    {
                        NSLog(@"p yes");
                        nalu_size1 = nextStartcode;
                        nale_size2 = contentLen - nextStartcode - 4;///slice2的内容要减去前边的四个字节

                        bufferOneFream[oneFreamIndex + 4+ nalu_size1]=(nale_size2 & 0xff000000)>>24;
                        bufferOneFream[oneFreamIndex + 5+ nalu_size1] = (nale_size2 & 0x00ff0000)>>16;
                        bufferOneFream[oneFreamIndex + 6+ nalu_size1] = (nale_size2 & 0x0000ff00)>>8;
                        bufferOneFream[oneFreamIndex + 7+ nalu_size1] = (nale_size2 & 0x000000ff);
                        ////////
                        imgLenArr[0] = (nalu_size1  & 0xff000000)>>24;                           //大端码流长度转换
                        imgLenArr[1] = (nalu_size1  & 0x00ff0000)>>16;
                        imgLenArr[2] = (nalu_size1  & 0x0000ff00)>>8;
                        imgLenArr[3] = nalu_size1  & 0x000000ff;

                        contentFream = (unsigned char*)malloc(contentLen + 4);     //转换成标准格式的一帧数据
                        memcpy(contentFream,imgLenArr, 4);
                        memcpy(contentFream+4 ,framePtr, contentLen);
                        NSLog(@"buffFreanLen %d,contentLen %d,nalu_size1 %d,nale_size2 %d",buffFreanLen,contentLen,nalu_size1,nale_size2);
                        pixelbuffer = HardDecode(contentFream, contentLen+4, spsBuff, spsLen, ppsBuff, ppsLen,0);
                        //保存yuv数据///////////////////////////////////////////////////////////////////
                        //SaveYUV(pixelbuffer, fout);
                        multislice = false ;
                        free(contentFream);
                        return pixelbuffer ;
                    }else{///p slgnal slice
                        NSLog(@"p no");
                        imgLenArr[0] = (contentLen & 0xff000000)>>24;                           //大端码流长度转换
                        imgLenArr[1] = (contentLen & 0x00ff0000)>>16;
                        imgLenArr[2] = (contentLen & 0x0000ff00)>>8;
                        imgLenArr[3] = contentLen & 0x000000ff;

                        contentFream = (unsigned char*)malloc(contentLen + 4);     //转换成标准格式的一帧数据
                        memcpy(contentFream,imgLenArr, 4);
                        memcpy(contentFream+4 ,framePtr, contentLen);

                        pixelbuffer = HardDecode(contentFream, contentLen+4, spsBuff, spsLen, ppsBuff, ppsLen,0);
                        //保存yuv数据///////////////////////////////////////////////////////////////////
                        //SaveYUV(pixelbuffer, fout);

                        free(contentFream);
                        return pixelbuffer ;
                    }

                }

                default://other,

                    break;

            }

原文地址:https://www.cnblogs.com/8335IT/p/12708308.html

时间: 04-14

关于iOS硬解码遇到多slice的视频码流的相关文章

H.264格式,iOS硬编解码 以及 iOS 11对HEVC硬编解码的支持

H.264格式,iOS硬编解码 以及 iOS 11对HEVC硬编解码的支持 1,H.264格式 网络表示层NAL,如图H.264流由一帧一帧的NALU组成: SPS:序列参数集,作用于一系列连续的编码图像: PPS:图像参数集,作用于编码视频序列中一个或多个独立的图像: 这两个帧也是独立的NALU. I-Frame:关键帧,帧内编码后的帧,显示比较完全的一帧: P-Frame:参考前一帧,可能只是对比前一帧的运动估计的变化部分: B-Frame:会参照前后的帧,其他类似P-Frame.B和P F

H.264硬编码&硬解码

Firefly-RK3288拥有强大的VPU(视像处理器),能够流畅实现720P和1080P视频的H.264编解码: 而H.264的压缩率更高,可以更大程度更小视频的空间占用. 详细看视频演示 1. 演示介绍 基于Firefly开发板:视频监控演示: 需要两块开发板:一块开发板摄像头采集+硬编码,网络传输. 另一块开发板 网络接收.硬解码+显示. Demo中采样5GHz Wi-Fi传输,摄像头使用OV13850,或UVC camera 2. H.264技术介绍 H.264是一种高性能视频编解码技

【GPU编解码】GPU硬解码---DXVA

[GPU编解码]GPU硬解码---DXVA 前面介绍利用NVIDIA公司提供的CUVID库进行视频硬解码,下面将介绍利用DXVA进行硬解码. 一.DXVA介绍 DXVA是微软公司专门定制的视频加速规范,是一种接口规范.DXVA规范制定硬件加速解码可分四级:VLD,控制BitStream:IDCT,反余弦变换:Mocomp,运动补偿,Pixel Prediction:PostProc,显示后处理.其中,VLD加速等级最高,所以其包含IDCT.MoCoopm和PostProc:IDCT加速次之,包含

Android中软解码和硬解码的优先级

我们先来看一下Android系统中解码器的命名,软解码器通常是以OMX.google开头的.硬解码器通常是以OMX.[hardware_vendor]开头的,比如TI的解码器是以OMX.TI开头的.当然还有一些不遵守这个命名规范的,不以OMX.开头的,那也会被认为是软解码器. 判断规则见frameworks/av/media/libstagefright/OMXCodec.cpp: static bool IsSoftwareCodec(const char *componentName) {

【GPU编解码】GPU硬解码---DXVA (转)

前面介绍利用NVIDIA公司提供的CUVID库进行视频硬解码,下面将介绍利用DXVA进行硬解码. 一.DXVA介绍 DXVA是微软公司专门定制的视频加速规范,是一种接口规范.DXVA规范制定硬件加速解码可分四级:VLD,控制BitStream:IDCT,反余弦变换:Mocomp,运动补偿,Pixel Prediction:PostProc,显示后处理.其中,VLD加速等级最高,所以其包含IDCT.MoCoopm和PostProc:IDCT加速次之,包含MoCoopm和PostProc:最后MoC

【GPU编解码】GPU硬解码---CUVID

问题描述:项目中,需要对高清监控视频分析处理,经测试,其解码过程所占CPU资源较多,导致整个系统处理效率不高,解码成为系统的瓶颈. 解决思路: 利用GPU解码高清视频,降低解码所占用CPU资源,加速解码过程. 一.OpenCV中的硬解码 OpenCV2.4.6中,已实现利用GPU进行读取视频,由cv::gpu::VideoReader_GPU完成,其示例程序如下. 1 int main(int argc, const char* argv[]) 2 { 3 if (argc != 2) 4 re

vlc源码分析(六) 调用OpenMAX硬解码H.265

http://www.cnblogs.com/jiayayao/p/6964506.html H.265(HEVC)编码格式能够在得到相同编码质量视频的前提下,使用相当于H.264(AVC)一半的存储容量,虽然H.265的算法复杂度比H.264高一个数量级,但是硬件水平在不断提高,因此H.265使用场合逐渐多了起来.好多硬件厂商芯片内部实现了H.265的硬解码.最近调试了vlc-android调用OpenMAX硬解码H.265的部分,使用的硬件平台是ZX-2000,系统是Android5.1.

什么是“软解码”,什么又是“硬解码”呢?

我们在计算机上播放的视频文件都是经过压缩的,因为这样有利于节约存储空间:那么在播放过程,就需要进行一个反射的解压缩过程.在以前这项工作都是由CPU来完成的,对于普通分辨率的AVI.RMVB等文件,绝大多数的CPU都可以胜任:但是发展到高清视频(1080i/p)之后,数据解压缩的工作量比以前翻了数倍,这让很多处理器叫苦不迭. 随着技术的发展,工程师们发现显卡的GPU/VPU要比CPU更适合这类大数据量的.低难度的重复工作.视频解码工作从处理器那里分离出来,交给显卡去做,这就叫做“硬解码”,例如NV

基于FFmpeg的Dxva2硬解码及Direct3D显示(三)

配置FFmpeg硬解码 设置解码输出格式回调 static AVPixelFormat GetHwFormat(AVCodecContext * pCodecCtx, const AVPixelFormat * pPixFmt) { // 因为采用的是DXVA2,所以这里直接写死了 return AV_PIX_FMT_DXVA2_VLD; } m_pDecoderCtx->get_format = GetHwFormat; 设置解码数据回调 // 个人理解就是将LPDIRECT3DSURFACE

例程:如何使用PX2硬解码H.264裸码流 [CODE_PX2]Decode_RAW_H264_FILE

Rayeager PX2开发板具有非常强大的多媒体处理能力,如果需要调用硬件加速针对普通媒体文件/码流进行解码,只需按照安卓标准调用多媒体相关接口即可. 针对一些行业用户的特殊需求,Rayeager PX2实际上也开放了接口可以对H.264等裸码流进行解码. 这里提供一份代码即可实现H.264裸码流的解码,如果您具有一定的Android系统开发经验,很快就能理解并进行相关改写.使用方法: 在PX2的Android编译环境根目录下将代码解压,并进入ChipSPARK_PX2_H264_DECODE