ios 硬解码h264视频的坑

作者: 南冠彤 | 来源:发表于2016-11-23 17:08 被阅读4712次

    最近这两天在写一个ios demo,用 VideoToolBox 硬解码从网络上实时接收过来的原始h264 nalu 数据(裸数据)。

    网络裸数据如下:


    后面的原始数据帧类似以上。。。

    (1)开始用了一个正常思路的方式:
    循环获取原始数据 -----> 分割大包中的nalu并单个送入硬解码 (nalu的前四个字节需要做调整:
    由00 00 00 01 变为大端的 nalu 的长度(不包括头四个字节)) 
    

    发现一个奇异现象:在模拟器中基本可以正常显示,偶尔有点水波现象,但是基本是正常的,不影响观看,然后部署到真机(iphone6-ios 9.3)中,出现绿屏或者出现一半正常显示一半出现绿屏。

    大致代码如下:

    /** Find the beginning and end of a NAL (Network Abstraction Layer) unit in 
    a byte buffer containing H264 bitstream data. 
    @param[in]   buf        the buffer 
    @param[in]   size       the size of the buffer 
    @param[out]  nal_start  the beginning offset of the nal 
    @param[out]  nal_end    the end offset of the nal 
    @return                 the length of the nal, or 0 if did not find start of nal, or -1 if did not find end of nal */
    static int findNalUnit(uint8_t* buf, int size, int* nal_start, int* nal_end)
    {
        int i;
        // find start
        *nal_start = 0;
        *nal_end = 0;
        i = 0;
        while (   //( next_bits( 24 ) != 0x000001 && next_bits( 32 ) != 0x00000001 ) 
              (buf[i] != 0 || buf[i+1] != 0 || buf[i+2] != 0x01) &&
              (buf[i] != 0 || buf[i+1] != 0 || buf[i+2] != 0 || buf[i+3] != 0x01)
               )
        { 
            i++; // skip leading zero
            if (i+4 >= size)
            {
                return 0;
            } // did not find nal start
        }
        if  (buf[i] != 0 || buf[i+1] != 0 || buf[i+2] != 0x01) // ( next_bits( 24 ) != 0x000001 )
        {
            i++;
        }
        if  (buf[i] != 0 || buf[i+1] != 0 || buf[i+2] != 0x01)
        {
            /* error, should never happen */
            return 0;
        }
        i+= 3;
        *nal_start = i;
        while (   //( next_bits( 24 ) != 0x000000 && next_bits( 24 ) != 0x000001 )
               (buf[i] != 0 || buf[i+1] != 0 || buf[i+2] != 0) &&
               (buf[i] != 0 || buf[i+1] != 0 || buf[i+2] != 0x01)
               )
        {
            i++;
            // FIXME the next line fails when reading a nal that ends exactly at the end of the data
            if (i+3 >= size) 
            {
                *nal_end = size;
                return (*nal_end - *nal_start);//return -1;
            } // did not find nal end, stream ended first
        }
        *nal_end = i;
        return (*nal_end - *nal_start);
    }
    
    - (BOOL)decodeNalu:(uint32_t)frame withSize(uint32_t)frameSize {
        // LOGD(@">>>>>>>>>>开始解码");
    
        if (frame == NULL || frameSize == 0)
            return NO;
    
        uint8_t* p = frame, *pf;
        size_t sz = frameSize;
        int nal_start, nal_end;
    
        while (![[NSThread currentThread] isCancelled] && findNalUnit(p, sz, &nal_start, &nal_end) > 0) {
            CVPixelBufferRef pixelBuffer = NULL;
            int nalu_type = p[nal_start] & 0x1f;
            int nal_len = nal_end - nal_start;
            uint8_t *pnal_size = (uint8_t*)(&nal_len);
            //{(uint8_t)(nal_len >> 24), (uint8_t)(nal_len >> 16), (uint8_t)(nal_len >> 8), (uint8_t)nal_len};
            if (nal_start == 3) { //big-endian
                p[-1] = *(pnal_size + 3);
                p[0]  = *(pnal_size + 2);
                p[1]  = *(pnal_size + 1);
                p[2]  = *(pnal_size);
                pf = p - 1;
            }
            else if (nal_start == 4) {
                p[0] = *(pnal_size + 3);
                p[1] = *(pnal_size + 2);
                p[2] = *(pnal_size + 1);
                p[3] = *(pnal_size);
                pf = p;
            }
            switch (nalu_type)
            {
                case 0x05:
                    LOGD(@"nalu_type:%d Nal type is IDR frame", nalu_type);
                    if ([self initH264Decoder]) {
                        pixelBuffer = [self decode:pf withSize:(nal_len + 4)];
                    }
                    break;
                case 0x07:
                    LOGD(@"nalu_type:%d Nal type is SPS", nalu_type);
                    if (_sps == NULL) {
                        _spsSize = nal_len;
                        _sps = (uint8_t*)malloc(_spsSize);
                        memcpy(_sps, &pf[4], _spsSize);
                    }
                    break;
                case 0x08:
                    LOGD(@"nalu_type:%d Nal type is PPS", nalu_type);
                    if (_pps == NULL) {
                        _ppsSize = nal_len;
                        _pps = (uint8_t*)malloc(_ppsSize);
                        memcpy(_pps, &pf[4], _ppsSize);
                    }
                    break;
                default:
                    LOGD(@"nalu_type:%d Nal type is B/P frame", nalu_type);
                    if ([self initH264Decoder]) {
                        pixelBuffer = [self decode:pf withSize:(nal_len + 4)];
                    }
                    break;
           }
            p += nal_start;
            p += nal_len;
            sz -= nal_end;
        }
    
    (2)另外一种方式:
    循环获取原始数据 -----> 重新打包大包中的nalu (每个nalu的头部改为当前nalu的长度)
    

    使用这种方式在模拟器和真机上都是正常显示的,没有花屏和绿屏现象。所以,对于获取到的一帧数据可能被分成了多个nalu,解码的时候不需要再拆分成单个nalu单独去解码,这样硬解码器内部认为此单nalu不是一个完整的帧,导致花屏。

    大致代码如下:

    //
    //  WBH264Play.m
    //  wenba_rtc
    //
    //  Created by zhouweiwei on 16/11/20.
    //  Copyright © 2016年 zhouweiwei. All rights reserved.
    //
    
    #import <Foundation/Foundation.h>
    #import "WBH264Play.h"
    
    #define kH264outputWidth  160
    #define kH264outputHeight 120
    
    static const uint8_t *avc_find_startcode_internal(const uint8_t *p, const uint8_t *end)
    {
        const uint8_t *a = p + 4 - ((intptr_t)p & 3);
        
        for (end -= 3; p < a && p < end; p++) {
            if (p[0] == 0 && p[1] == 0 && p[2] == 1)
                return p;
        }
        
        for (end -= 3; p < end; p += 4) {
            uint32_t x = *(const uint32_t*)p;
            //      if ((x - 0x01000100) & (~x) & 0x80008000) // little endian
            //      if ((x - 0x00010001) & (~x) & 0x00800080) // big endian
            if ((x - 0x01010101) & (~x) & 0x80808080) { // generic
                if (p[1] == 0) {
                    if (p[0] == 0 && p[2] == 1)
                        return p;
                    if (p[2] == 0 && p[3] == 1)
                        return p+1;
                }
                if (p[3] == 0) {
                    if (p[2] == 0 && p[4] == 1)
                        return p+2;
                    if (p[4] == 0 && p[5] == 1)
                        return p+3;
                }
            }
        }
        
        for (end += 3; p < end; p++) {
            if (p[0] == 0 && p[1] == 0 && p[2] == 1)
                return p;
        }
        
        return end + 3;
    }
    
    const uint8_t *avc_find_startcode(const uint8_t *p, const uint8_t *end)
    {
        const uint8_t *out= avc_find_startcode_internal(p, end);
        if(p<out && out<end && !out[-1]) out--;
        return out;
    }
    
    @interface H264HwDecoder()
    {
        NSThread *thread;
        uint8_t* _vdata;
        size_t _vsize;
    
        uint8_t *_buf_out; // 原始接收的重组数据包
    
        uint8_t *_sps;
        size_t _spsSize;
        uint8_t *_pps;
        size_t _ppsSize;
        VTDecompressionSessionRef _deocderSession;
        CMVideoFormatDescriptionRef _decoderFormatDescription;
    }
    @end
    
    @implementation H264HwDecoder
    
    //解码回调函数
    static void didDecompress(void *decompressionOutputRefCon, void *sourceFrameRefCon, OSStatus status, VTDecodeInfoFlags infoFlags, CVImageBufferRef imageBuffer, CMTime presentationTimeStamp, CMTime presentationDuration ) {
    
        if (status != noErr || imageBuffer == nil) {
            LOGE(@"Error decompresssing frame at time: %.3f error: %d infoFlags: %u",
                 presentationTimeStamp.value/presentationTimeStamp.timescale, status, infoFlags);
            return;
        }
        
        if (kVTDecodeInfo_FrameDropped & infoFlags) {
            LOGW(@"video frame droped");
            return;
        }
    
    //    int i,j;
    //    if (CVPixelBufferIsPlanar(imageBuffer)) {
    //        i  = (int)CVPixelBufferGetWidthOfPlane(imageBuffer, 0);
    //        j = (int)CVPixelBufferGetHeightOfPlane(imageBuffer, 0);
    //    } else {
    //        i  = (int)CVPixelBufferGetWidth(imageBuffer);
    //        j = (int)CVPixelBufferGetHeight(imageBuffer);
    //    }
        
        __weak H264HwDecoder *decoder = (__bridge H264HwDecoder *)decompressionOutputRefCon;
        if (decoder.delegate != nil) {
            CVPixelBufferRef *outputPixelBuffer = (CVPixelBufferRef *)sourceFrameRefCon;
            *outputPixelBuffer = CVPixelBufferRetain(imageBuffer);
            [decoder.delegate displayDecodedFrame:decoder.uid imageBuffer:imageBuffer];
        }
    }
    
    - (BOOL)open:width:(uint16_t)width height:(uint16_t)height (NSObject<IWBH264HwDecoderDelegate>*)displayDelegate {
        
        [self close];
    
        if (width == 0 || height == 0) {
            _out_width = kH264outputWidth;
            _out_height = kH264outputHeight;
        }
        else {
            _out_width = width;
            _out_height = height;
        }
        _vsize = _out_width * _out_height * 3;
        _vdata = (uint8_t*)malloc(_vsize * sizeof(uint8_t));
    
        _buf_out = (uint8_t*)malloc(_out_width * _out_height * sizeof(uint8_t));
    
        self.delegate = displayDelegate;
    
        thread = [[NSThread alloc] initWithTarget:self selector:@selector(run) object:nil];
        //thread.name = @"Thread";
        [thread start];
    
        return YES;
    }
    
    - (void)setH264DecoderInterface:(NSObject<IWBH264HwDecoderDelegate>*)displayDelegate {
        self.delegate = displayDelegate;
    }
    
    - (void)run {
        size_t out_size = 0;
    
        while (![[NSThread currentThread] isCancelled]) {
            /*这里从网路端循环获取视频数据*/
            if (api_video_get(_uid, _vdata, &out_size) == 0 && out_size > 0) {
                if ([self decodeNalu:_vdata withSize:out_size]) {
                }
            }
    
            [NSThread sleepForTimeInterval:0.005];
        }
    }
    
    - (void)stop {
        LOGD(@"uid:%u decoder stop", _uid);
    
        if (_thread != nil) {
            if (!_thread.isCancelled) {
                [_thread cancel];
                LOGD(@"uid:%u thread cancel", _uid);
            }
        }
        
        LOGD(@"uid:%u decoder stoped", _uid);
    
        if (_decoderFormatDesc != nil) {
            CFRelease(_decoderFormatDesc);
            _decoderFormatDesc = nil;
        }
    
        if (_deocderSession != nil) {
            VTDecompressionSessionWaitForAsynchronousFrames(_deocderSession);
            VTDecompressionSessionInvalidate(_deocderSession);
            CFRelease(_deocderSession);
            _deocderSession = nil;
        }
    
        _uid = 0;
    
        _out_width = kH264outputWidth;
        _out_height = kH264outputHeight;
    
        if (_vdata != NULL) {
            free(_vdata);
            _vdata = NULL;
            _vsize = 0;
        }
    
        if (_sps != NULL) {
            free(_sps);
            _sps = NULL;
            _spsSize = 0;
        }
    
        if (_pps != NULL) {
            free(_pps);
            _pps = NULL;
            _ppsSize = 0;
        }
    
        if (_buf_out != NULL) {
            free(_buf_out);
            _buf_out = NULL;
        }
    
        self.delegate = nil;
    }
    
    - (void)close {
        [self stop];
        _thread = nil;
    
        LOGD(@"uid:%u decoder close", _uid);
    }
    
    -(BOOL)initH264Decoder {
        if (_deocderSession) {
            return YES;
        }
    
        if (!_sps || !_pps || _spsSize == 0 || _ppsSize == 0) {
            return NO;
        }
    
        const uint8_t* const parameterSetPointers[2] = { _sps, _pps };
        const size_t parameterSetSizes[2] = { _spsSize, _ppsSize };
        OSStatus status = CMVideoFormatDescriptionCreateFromH264ParameterSets(kCFAllocatorDefault,
                                                                              2, //param count
                                                                              parameterSetPointers,
                                                                              parameterSetSizes,
                                                                              4, //nal start code size
                                                                              &_decoderFormatDescription);
        if (status == noErr) {
            NSDictionary* destinationPixelBufferAttributes = @{
                                                               (id)kCVPixelBufferPixelFormatTypeKey : [NSNumber numberWithInt:kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange]
                                                               //硬解必须是 kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange 或者是kCVPixelFormatType_420YpCbCr8Planar
                                                               //因为iOS是nv12  其他是nv21
                                                               , (id)kCVPixelBufferWidthKey  : [NSNumber numberWithInt:kH264outputWidth]
                                                               , (id)kCVPixelBufferHeightKey : [NSNumber numberWithInt:kH264outputHeight]
                                                               //, (id)kCVPixelBufferBytesPerRowAlignmentKey : [NSNumber numberWithInt:kH264outputWidth*2]
                                                               , (id)kCVPixelBufferOpenGLCompatibilityKey : [NSNumber numberWithBool:NO]
                                                               , (id)kCVPixelBufferOpenGLESCompatibilityKey : [NSNumber numberWithBool:YES]
                                                               };
    
            VTDecompressionOutputCallbackRecord callBackRecord;
            callBackRecord.decompressionOutputCallback = didDecompress;
            callBackRecord.decompressionOutputRefCon = (__bridge void *)self;
    
            status = VTDecompressionSessionCreate(kCFAllocatorDefault,
                                                  _decoderFormatDescription,
                                                  NULL,
                                                  (__bridge CFDictionaryRef)destinationPixelBufferAttributes,
                                                  &callBackRecord,
                                                  &_deocderSession);
            VTSessionSetProperty(_deocderSession, kVTDecompressionPropertyKey_ThreadCount, (__bridge CFTypeRef)[NSNumber numberWithInt:1]);
            VTSessionSetProperty(_deocderSession, kVTDecompressionPropertyKey_RealTime, kCFBooleanTrue);
        } else {
            LOGE(@"reset decoder session failed status=%d", status);
            return NO;
        }
        
        return YES;
    }
    
    - (BOOL)resetH264Decoder {
        if(_deocderSession) {
            VTDecompressionSessionWaitForAsynchronousFrames(_deocderSession);
            VTDecompressionSessionInvalidate(_deocderSession);
            CFRelease(_deocderSession);
            _deocderSession = NULL;
        }
        return [self initH264Decoder];
    }
    
    - (CVPixelBufferRef)decode:(uint8_t *)frame withSize:(uint32_t)frameSize {
        if (frame == NULL || _deocderSession == nil)
            return NULL;
    
        CVPixelBufferRef outputPixelBuffer = NULL;
        CMBlockBufferRef blockBuffer = NULL;
    
        OSStatus status  = CMBlockBufferCreateWithMemoryBlock(NULL,
                                                              (void *)frame,
                                                              frameSize,
                                                              kCFAllocatorNull,
                                                              NULL,
                                                              0,
                                                              frameSize,
                                                              FALSE,
                                                              &blockBuffer);
        if(status == kCMBlockBufferNoErr) {
            CMSampleBufferRef sampleBuffer = NULL;
            const size_t sampleSizeArray[] = {frameSize};
    //        status = CMSampleBufferCreateReady(kCFAllocatorDefault,
    //                                           blockBuffer,
    //                                           _decoderFormatDescription ,
    //                                           1, 0, NULL, 1, sampleSizeArray,
    //                                           &sampleBuffer);
            status = CMSampleBufferCreate(NULL, blockBuffer, TRUE, 0, 0, _decoderFormatDescription, 1, 0, NULL, 0, NULL, &sampleBuffer);
    
            if (status == kCMBlockBufferNoErr && sampleBuffer) {
                VTDecodeFrameFlags flags = 0;
                VTDecodeInfoFlags flagOut = 0;
                status = VTDecompressionSessionDecodeFrame(_deocderSession,
                                                           sampleBuffer,
                                                           flags,
                                                           &outputPixelBuffer,
                                                           &flagOut);
    
                if (status == kVTInvalidSessionErr) {
                    LOGE(@"Invalid session, reset decoder session");
                    [self resetH264Decoder];
                } else if(status == kVTVideoDecoderBadDataErr) {
                    LOGE(@"decode failed status=%d(Bad data)", status);
                } else if(status != noErr) {
                    LOGE(@"decode failed status=%d", status);
                }
            }
    
            if (sampleBuffer != NULL)
                CFRelease(sampleBuffer);
        }
        if (blockBuffer != NULL)
            CFRelease(blockBuffer);
        
        return outputPixelBuffer;
    }
    
    - (BOOL)decodeNalu:(uint8_t *)frame withSize:(uint32_t)frameSize {
        // LOGD(@">>>>>>>>>>开始解码");
    
        if (frame == NULL || frameSize == 0)
            return NO;
    
        int size = frameSize;
        const uint8_t *p = frame;
        const uint8_t *end = p + size;
        const uint8_t *nal_start, *nal_end;
        int nal_len, nalu_type;
    
        size = 0;
        nal_start = avc_find_startcode(p, end);
        while (![[NSThread currentThread] isCancelled]) {
            while (![[NSThread currentThread] isCancelled] && nal_start < end && !*(nal_start++));
            if (nal_start == end)
                break;
    
            nal_end = avc_find_startcode(nal_start, end);
            nal_len = nal_end - nal_start;
            
            nalu_type = nal_start[0] & 0x1f;
            if (nalu_type == 0x07) {
                if (_sps == NULL) {
                    _spsSize = nal_len;
                    _sps = (uint8_t*)malloc(_spsSize);
                    memcpy(_sps, nal_start, _spsSize);
                }
            }
            else if (nalu_type == 0x08) {
                if (_pps == NULL) {
                    _ppsSize = nal_len;
                    _pps = (uint8_t*)malloc(_ppsSize);
                    memcpy(_pps, nal_start, _ppsSize);
                }
            }
            else {
                _buf_out[size + 0] = (uint8_t)(nal_len >> 24);
                _buf_out[size + 1] = (uint8_t)(nal_len >> 16);
                _buf_out[size + 2] = (uint8_t)(nal_len >> 8 );
                _buf_out[size + 3] = (uint8_t)(nal_len);
    
                memcpy(_buf_out + 4 + size, nal_start, nal_len);
                size += 4 + nal_len;
            }
    
            nal_start = nal_end;
        }
    
        if ([self initH264Decoder]) {
            CVPixelBufferRef pixelBuffer = NULL;
            pixelBuffer = [self decode:_buf_out withSize:size];
        }
    
        return size > 0 ? YES : NO;
    }
    
    @end
    
    注意:ios 的视频部分坑很多,需要自己亲身实践才能得之精髓,另外需要结合Apple 的官方例子进行测试可以事半功倍。
    
    下一篇打算讲解下《ios硬编码h264视频设置帧率的坑》,此部分网络上基本没有正确的解答,比如采集摄像头
    30fps,设置成12fps,基本都是不生效的,这里面有点坑。
    
    发私信要demo代码的太多了,为了服务更好,可以合作。
    并提供各种推拉流、编解码、播放相关等一体化的流媒体直播、点播解决方案。
    

    相关文章

      网友评论

      • ca02f96a7517:哥们,解码的时候可以设置解码时候的帧率跟码率的吗?
        南冠彤:@ca02f96a7517 解码码率设置不了
      • 疯狂一生:我想问下,为啥我的代码总是崩溃在这句memcpy(_buf_out + 4 + size, nal_start, nal_len);数据流没有P帧的时候,这句代码没事,可以正确解码,但是加了P帧之后,就会崩溃
        南冠彤:可能你解析出来的长度不对,导致memcpy溢出
      • 小峰o:用socket接收服务器给的原始h264 nalu 数据(裸数据),发现cup使用率与内存使用率不断的增加。
        小峰o:我自己的socket数据处理的问题,现在已经解决
      • o_Cooper_o:您好, 发下代码吗,非常需要。 HKZ516@163.com
      • Old船长:最近也在学习这方面的知识,求源码:516045397@qq.com!

        多谢!
      • o_Cooper_o:您好, 能发下 demo吗 邮箱: HKZ516@163.com
      • 28d7ed62c7b3:大神,求源码一份,184069449@qq.com
      • 不辣先生:大神,麻烦给个资源学习下,谢谢,594936451@qq.com
      • stevenzhan:大神,我最近在弄视频这块的,遇到的问题很类似,求源码一份,🙏。邮箱:492653690@qq.com
        stevenzhan:邮箱地址错了,492653590@qq.com
      • 遇见猫的大鱼:大神,求demo 。我拿到的也是h264 nalu 数据。 835066041@qq.com
      • d0caed4b9235:想要一份源代码,邮箱:grj0204@126.com 谢谢!
      • d0caed4b9235:想要一份源代码,邮箱: grj0204@126.com
      • Liusr:硬解码很多绿屏。。。。怎么回事
        Liusr:@南冠彤 是的,一帧一帧的收然后送,我之前保存过收到的视频数据,vlc播放没问题。但是手机解码播放就会绿屏。你有没有研究过是不是videotoolbox支持的问题。
        南冠彤:@Liu_sr 确保送进去的每帧和nalu是正确的
      • 爱勤海之旅:楼主,来份demo,邮箱:1067452293@qq.com
      • 叉叉歪大魔王:求个demo 245718977@qq.com 我是个新手 现在在尝试解h264文件 现在解出来的图片顺序都是乱的 是264文件里面没带pts信息吗 是不是只能自己计算pts 还有想问的就是播放mp4的话需要解封装,一般是FFmpeg来解封装还是自己写,还有就是播放器的解码策略,是一次解码完 还是有定时器来解 还有拖拽进度条又是怎么处理的
        南冠彤:@叉叉歪大魔王 有偿提供完整的demo
      • quietloss:大佬处理annexbToavcc格式的代码相当不错,正好缺这方面资料 看h264协议看的蛋疼:yum: 本来想从ffmpeg源码里扣的有这份参考太好了
      • DSperson:大婶 请问 已经存在相册中的视频 取出来如何转为h264?
        南冠彤:@DSperson 首先你要parser 你的视频文件,然后分离出audio 和 video, 再对video 做parser(如果就是h264那直接 获取就行,如果不是的话需要转码成 h264)
        DSperson:@南冠彤 大婶稍微详细点:flushed:
        南冠彤:读--->取--->分----解
      • f3261d6a3060:您好,我也遇到了同样的问题,您说的:“2)另外一种方式:
        循环获取原始数据 -----> 重新打包大包中的nalu (每个nalu的头部改为当前nalu的长度)”这句话我没太理解,能说详细一点吗
      • 35d3edac503f:请问解码的时候播放速度怎么控制啊?
        35d3edac503f:@南冠彤 可以说具体点吗?
        南冠彤:可以通过时间戳来控制
      • d2768f6b44b8:你好能发一份demo给我吗? 30513207@qq.com
      • Th丶小伟:楼主,ios硬编码h264视频设置帧率的坑 什么时候写呀
        南冠彤:@阿布_8f76 哈哈,会写的~
        35d3edac503f:@南冠彤 我都等了你一年了
        南冠彤:@Th丶小伟 有空了再写,哈哈
      • KeyboardDirver:哥们,麻烦给我发一个demo被 269004257@qq.com
      • 帆123:你好,我想请教两点关于编码的问题
        1、H264HwEncoderImpl* encoder = (__bridge H264HwEncoderImpl*)outputCallbackRefCon;这个会出现野指针崩溃,在didCompressH264的回调里面的,不知道改如何判断野指针的问题。
        2、编码时VTCompressionSessionCreate会概率性返回错误
        请问大神,以上两个问题可有解决方法?
      • 95bb1f8adaf4:你好可以给分享代码吗?luson2015@126.com
      • McDan:求一套硬解码demo,谢谢 651973001@qq.com
      • 水达:你好,发一份代码吧! 493397476@qq.com
      • Liusr:公司有多种设备包括录制电脑屏幕的方式,生成H264码流。但是用硬解码有的可以显示,有的就是一半正常一半绿屏。解码的流程就这么几步,我还应该怎么办呢???
      • 华少主:大神,我的邮箱是 992173504@qq.com,麻烦发一个demo给我,谢谢!
      • followme_8d8f:博主,最近做这方面的事情,首先网络的流数据不是标准的nalu数据 ,其次数据也不知道怎么拆分的,能把demo发一下吗,630533914@qq.com,可能 请加qq.
      • KeyboardLife:width = 640 heigh = 480,屏幕绿屏,横屏且没占满,两边是黑色的,中间绿屏,为啥呢
        KeyboardLife:@南冠彤 之前服务器传过来的是1280*720 客户端显示的是好的,现在服务器改成640*480的,代码没动过客户端绿屏,安卓说是sps pps解析的问题,同一套代码没动过,我需要改哪些来适配服务器的640*480,能加QQ聊吗
        南冠彤:@KeyboardLife 你可以根据屏幕分辨率来做输出视频尺寸的调整,; 中间绿屏应该是丢i帧或者帧数据部分丢失
      • KeyboardLife:视频监控,偶现花屏,请问您是如何处理的??
        KeyboardLife:@南冠彤 服务器发的时候数据帧是完整的,传输到客户端丢帧了,你会如何做处理呢,视频雪花是不是因为p帧丢失导致的呢
        南冠彤:@KeyboardLife 确保数据帧完整
      • 温暖C:大神,我之前用的是ffmpeg,现在想改成硬解码,麻烦发一份代码到我邮箱:2396235299@qq.com,谢谢
        南冠彤:@温暖C 主要代码就是这了
      • 甲子湖畔的扫地僧:您好,最近在做这一块,有时start code是0000 01,有时是0000 0001,不知道怎么去处理了。也想看看您的源码,方便的话您给我发一份源码吧,master2017@163.com。谢谢
      • 忘情_m:LZ你好,能发一份源码给我么?我通过GCDSocket得到了裸码流,但是却不发解码,谢谢,2851133868@qq.com
      • eae953a95511:您好,方便得话给一个代码吧:smile: 253407916@qq.com 谢谢~
      • KevinZhangGnu:请问,avc_find_startcode_internal 方法中,const uint8_t *a = p + 4 - ((intptr_t)p & 3); 这句, p&3 怎么理解
      • o簡箪:用第二种方法为什么有时花屏,有时不花,求指教
        o簡箪:加下qq897741967
        o簡箪:@南冠彤 如果第一帧完整,就不花屏,如果开始第一帧缺少关键帧就花屏
        南冠彤:确保视频数据正确
      • o簡箪:if (api_vch_get(_uid, _vdata, &out_size) == 0 && out_size > 0)这句不懂,求指教
        南冠彤:@o簡箪 这个是从网络获取视频数据的api, 可自行定义
      • o簡箪:楼主,求一份demo,解决花屏的,谢谢!897741967@qq.com
        南冠彤:先确认视频数据是否正确,并且startcode 解析并设置成长度正确, 代码都在文章里
      • 爬向天花板的蚂蚁:大神,给份代码,谢谢。chen409303584@163.com
        南冠彤:@爬向天花板的蚂蚁代码都在文章里哈
      • 小_菜_鸟:请问一下 解码后的视频 如何设置长宽比!比如4:3 或 16:9
        南冠彤:@小_菜_鸟 都是硬解, 上面部分是不正确的方式
        小_菜_鸟:@南冠彤 本文上下两部分代码 是不是 上边是软解 下边是硬解码
        南冠彤:可以在VTDecompressionSessionCreate 这个函数的第四个参数videoDecoderSpecification指定你想要的 kCVPixelBufferWidthKey kCVPixelBufferHeightKey
      • 小_菜_鸟:写的很详细 可惜看不太懂 注释多点就好了
        hebeisuifeng@163.com 谢了!
        南冠彤:解码的代码都在文章里写了, app部分需要自己弄下
      • Mo丿小冷:求份代码,谢谢了392056807@qq.com
        南冠彤:@Mo丿小冷 恩,解决就好
        Mo丿小冷:@南冠彤 搞定了,谢谢大神
        南冠彤:@Mo丿小冷 代码基本就是这些哈, 集成到自己的app里做个测试就可以
      • NicWhite:请问一下,我解本地文件能正常解出来,解实时码流总是解码报错 -12911,偶尔会编程-12902,请问是怎么情况
        许暹罗:@南冠彤 -12911的问题搞定了么?
        南冠彤:看看你的参数设置是否正确, sps pps
      • 郭炜12045:求份demo 549202090@qq.com 谢谢
      • meng_huang:1217014383@qq.com 谢谢啦
        o_Cooper_o:您好, 有代码吗
      • cc77cdbc102c:大神你好,麻烦发一下你的代码,我想参考下,邮箱:827930644@qq.com,谢谢!
        o_Cooper_o:您好, 有代码吗
      • ybananaice:934722839@qq.com 求源码参考一下
        o_Cooper_o:您好, 有代码吗
      • 温暖C:大神,我现在在研究硬解码,麻烦发一下代码,谢谢! 邮箱:1442485516@qq.com
        o_Cooper_o:您好, 有代码吗
      • 南冠彤:大家如果有不明白的地方可以留言, 主要的核心代码就是这些了
        冰箱里的一条咸鱼:您好,我是Mac开发的,在解码H264码流的时候,出现解码出来的全屏绿色的情况,请问该怎么解决这种问题?能否提供一种思路,谢谢了
        南冠彤:@S__L 判断是三字节开始码还是四字节开始码,如果out[-1] = 0,则代表00 00 00 01,否则00 00 01
        S__L:const uint8_t *avc_find_startcode(const uint8_t *p, const uint8_t *end)
        {
        const uint8_t *out= avc_find_startcode_internal(p, end);
        if(p<out && out<end && !out[-1]) out--;
        return out;
        } 这里没看懂啊,能解释一下么?
      • 6f079e8cec2d:大神....请发给我啊,谢谢啊.....791799413@qq.com
      • wnido:大神,发份代码给我: guiyan92010@163.com O(∩_∩)O谢谢!
      • 南冠彤:如果有想要代码的,可以留个邮箱等
        远方的枸杞:大神,我最近在研究这个.......请发给我啊,谢谢啊.....1007793974@qq.com
        moreFine:@南冠彤 最近正想补充一下关于视屏硬解码的知识,谢谢。990802260@qq.com
        857e81f3c5b6:我这边是读本地的H264 我看到你有判断startcode 我从本地读 怎么判断startcode 先看你的代码学习下 373018494@qq.com

      本文标题:ios 硬解码h264视频的坑

      本文链接:https://www.haomeiwen.com/subject/sgsvpttx.html