美文网首页
iOS 硬解H264

iOS 硬解H264

作者: Krien | 来源:发表于2017-10-23 09:23 被阅读140次

    公司摄像机产品客户端需要添加iOS硬解码,解码原始摄像头数据,适合解码h264的数据!需要的朋友可以参考下,贴上硬解代码

    -(CVPixelBufferRef)deCompressedCMSampleBufferWithData:(AVFrameData *)frameData andOffset:(int)offset
    {
        NALUnit nalUnit;
        CVPixelBufferRef pixelBufferRef =NULL;
        char *data = (char*)frameData->getData();
        int dataLen = frameData->getDataLen();
    
        if(data ==NULL || dataLen ==0){
            returnNULL;
        }
    
        while([selfnalunitWithData:dataandDataLen:dataLen andOffset:offsettoNALUnit:&nalUnit])
        {
            if(nalUnit.data ==NULL || nalUnit.size ==0){
                returnNULL;
            }
            
            pixelBufferRef = NULL;
            [selfinfalteStartCodeWithNalunitData:&nalUnit];
            NSLog(@"NALUint Type: %d.", nalUnit.type);
            
            switch (nalUnit.type) {
                caseNALUTypeIFrame://IFrame
                    if(_sps &&_pps)
                    {
                        if([selfinitH264Decoder]){
                            pixelBufferRef = [selfdecompressWithNalUint:nalUnit];
                            NSLog(@"NALUint I Frame size:%d", nalUnit.size);
                            
                            free(_sps);
                            free(_pps);
                            _pps =NULL;
                            _sps =NULL;
                            return pixelBufferRef;
                        }
                    }
                    break;
                caseNALUTypeSPS://SPS
                    _spsSize = nalUnit.size -4;
                    if(_spsSize <=0){
                        NSLog(@"_spsSize is null");
                        returnNULL;
                    }
                    
                    _sps = (uint8_t*)malloc(_spsSize);
                    memcpy(_sps, nalUnit.data +4,_spsSize);
                    NSLog(@"NALUint SPS size:%d", nalUnit.size -4);
                    break;
                caseNALUTypePPS://PPS
                    _ppsSize = nalUnit.size -4;
                    if(_ppsSize <=0){
    //                    NSLog(@"_ppsSize is null");
                        returnNULL;
                    }
                    
                    _pps = (uint8_t*)malloc(_ppsSize);
                    memcpy(_pps, nalUnit.data +4,_ppsSize);
                    NSLog(@"NALUint PPS size:%d", nalUnit.size -4);
                    break;
                caseNALUTypeBPFrame://B/P Frame
                    pixelBufferRef = [selfdecompressWithNalUint:nalUnit];
                    NSLog(@"NALUint B/P Frame size:%d", nalUnit.size);
                    return pixelBufferRef;
                default:
                    break;
            }
            
            offset += nalUnit.size;
            if(offset >= dataLen){
                returnNULL;
            }
        }
        
        NSLog(@"The AVFrame data size:%d", offset);
        returnNULL;
    }
    
    -(void)infalteStartCodeWithNalunitData:(NALUnit *)dataUnit
    {
        //Inflate start code with data length
        unsignedchar* data  = dataUnit->data;
        unsignedint dataLen = dataUnit->size -4;
        
        data[0] = (unsignedchar)(dataLen >>24);
        data[1] = (unsignedchar)(dataLen >>16);
        data[2] = (unsignedchar)(dataLen >>8);
        data[3] = (unsignedchar)(dataLen &0xff);
    }
    
    -(int)nalunitWithData:(char *)data andDataLen:(int)dataLen andOffset:(int)offset toNALUnit:(NALUnit *)unit
    {
        unit->size =0;
        unit->data =NULL;
        
        int addUpLen = offset;
        while(addUpLen < dataLen)
        {
            if(data[addUpLen++] ==0x00 &&
               data[addUpLen++] == 0x00 &&
               data[addUpLen++] == 0x00 &&
               data[addUpLen++] == 0x01){//H264 start code
                
                int pos = addUpLen;
                while(pos < dataLen){//Find next NALU
                    if(data[pos++] ==0x00 &&
                       data[pos++] == 0x00 &&
                       data[pos++] == 0x00 &&
                       data[pos++] == 0x01){
                        break;
                    }
                }
                
                unit->type = data[addUpLen] &0x1f;
                if(pos == dataLen){
                    unit->size = pos - addUpLen +4;
                }else{
                    unit->size = pos - addUpLen;
                }
                
                unit->data = (unsignedchar*)&data[addUpLen -4];
                return1;
            }
        }
        return -1;
    }
    
    
    -(BOOL)initH264Decoder
    {
        if(_decompressionSession){
            returntrue;
        }
        
        constuint8_t *const parameterSetPointers[2] = {_sps,_pps};
        constsize_t parameterSetSizes[2] = {_spsSize,_ppsSize};
        OSStatus status =CMVideoFormatDescriptionCreateFromH264ParameterSets(kCFAllocatorDefault,
                                                                             2,//parameter count
                                                                              parameterSetPointers,
                                                                              parameterSetSizes,
                                                                             4,//NAL start code size
                                                                              &(_decompressionFormatDesc));
        if(status ==noErr){
            constvoid *keys[] = {kCVPixelBufferPixelFormatTypeKey};
            
            //kCVPixelFormatType_420YpCbCr8Planar is YUV420, kCVPixelFormatType_420YpCbCr8BiPlanarFullRange is NV12
            uint32_t biPlanarType =kCVPixelFormatType_420YpCbCr8BiPlanarFullRange;
            constvoid *values[] = {CFNumberCreate(NULL,kCFNumberSInt32Type, &biPlanarType)};
            CFDictionaryRef attributes =CFDictionaryCreate(NULL, keys, values,1,NULL,NULL);
            
            //Create decompression session
            VTDecompressionOutputCallbackRecord outputCallBaclRecord;
            outputCallBaclRecord.decompressionOutputRefCon =NULL;
            outputCallBaclRecord.decompressionOutputCallback =decompressionOutputCallbackRecord;
            status = VTDecompressionSessionCreate(kCFAllocatorDefault,
                                                  _decompressionFormatDesc,
                                                  NULL, attributes,
                                                  &outputCallBaclRecord,
                                                  &_decompressionSession);
            CFRelease(attributes);
            if(status !=noErr){
                returnfalse;
            }
        }else{
            NSLog(@"Error code %d:Creates a format description for a video media stream described by H.264 parameter set NAL units.", (int)status);
            returnfalse;
        }
        
        returntrue;
    }
    
    
    static void decompressionOutputCallbackRecord(void *CM_NULLABLE decompressionOutputRefCon,
                                                  void *CM_NULLABLE sourceFrameRefCon,
                                                  OSStatus status,
                                                  VTDecodeInfoFlags infoFlags,
                                                  CM_NULLABLECVImageBufferRef imageBuffer,
                                                  CMTime presentationTimeStamp,
                                                  CMTime presentationDuration ){
        CVPixelBufferRef *outputPixelBuffer = (CVPixelBufferRef *)sourceFrameRefCon;
        *outputPixelBuffer = CVPixelBufferRetain(imageBuffer);
    }
    
    -(CVPixelBufferRef)decompressWithNalUint:(NALUnit)dataUnit
    {
        CMBlockBufferRef blockBufferRef =NULL;
        CVPixelBufferRef outputPixelBufferRef =NULL;
        
        //1.Fetch video data and generate CMBlockBuffer
        OSStatus status =CMBlockBufferCreateWithMemoryBlock(kCFAllocatorDefault,
                                                             dataUnit.data,
                                                             dataUnit.size,
                                                             kCFAllocatorNull,
                                                             NULL,
                                                             0,
                                                             dataUnit.size,
                                                             0,
                                                             &blockBufferRef);
        //2.Create CMSampleBuffer
        if(status ==kCMBlockBufferNoErr){
            CMSampleBufferRef sampleBufferRef =NULL;
            constsize_t sampleSizes[] = {dataUnit.size};
            OSStatus createStatus =CMSampleBufferCreateReady(kCFAllocatorDefault,
                                                              blockBufferRef,
                                                              _decompressionFormatDesc,
                                                              1,
                                                              0,
                                                              NULL,
                                                              1,
                                                              sampleSizes,
                                                              &sampleBufferRef);
            
            //3.Create CVPixelBuffer
            if(createStatus ==kCMBlockBufferNoErr && sampleBufferRef){
                VTDecodeFrameFlags frameFlags =0;
                VTDecodeInfoFlags infoFlags =0;
                
                OSStatus decodeStatus =VTDecompressionSessionDecodeFrame(_decompressionSession,
                                                                          sampleBufferRef,
                                                                          frameFlags,
                                                                          &outputPixelBufferRef,
                                                                          &infoFlags);
                
                if(decodeStatus !=noErr){
                    CFRelease(sampleBufferRef);
                    CFRelease(blockBufferRef);
                    outputPixelBufferRef = nil;
                    return outputPixelBufferRef;
                }
                
                
                if(_isTakePicture){
                    if(!_isSaveTakePictureImage){
                        CIImage *ciImage = [CIImageimageWithCVPixelBuffer:outputPixelBufferRef];
                        CIContext *ciContext = [CIContextcontextWithOptions:nil];
                        CGImageRef videoImage = [ciContext
                                                 createCGImage:ciImage
                                                 fromRect:CGRectMake(0,0,
                                                                     CVPixelBufferGetWidth(outputPixelBufferRef),
                                                                     CVPixelBufferGetHeight(outputPixelBufferRef))];
                        
                        UIImage *uiImage = [UIImageimageWithCGImage:videoImage];
                        _isSaveTakePictureImage = [UIImageJPEGRepresentation(uiImage,1.0)writeToFile:_saveTakePicturePathatomically:YES];
                        CGImageRelease(videoImage);
                        _isTakePicture =false;
                    }
                }
                CFRelease(sampleBufferRef);
            }
            CFRelease(blockBufferRef);
        }
        return outputPixelBufferRef;
    }
    
    -(void)dealloc
    {
        if(_sps){
            free(_sps);
            _sps =NULL;
        }
        
        if(_pps){
            free(_pps);
            _pps =NULL;
        }
        
        if(_decompressionSession){
            CFRelease(_decompressionSession);
            _decompressionSession =NULL;
        }
        
        if(_decompressionFormatDesc){
            CFRelease(_decompressionFormatDesc);
            _decompressionFormatDesc =NULL;
        }
    }
    
    //消费
    -(void)enqueueSampleBuffer:(CMSampleBufferRef &)sampleBuffer playerView:(AVSampleBufferDisplayLayer *)layer{
        if (sampleBuffer){
            CFRetain(sampleBuffer);
            [layer enqueueSampleBuffer:sampleBuffer];
            CFRelease(sampleBuffer);
           
            if (layer.status ==AVQueuedSampleBufferRenderingStatusFailed){
                NSLog(@"ERROR: %@", layer.error);
                if (-11847 == layer.error.code){
    //                [self rebuildSampleBufferDisplayLayer];
                }
            }else{
    //          NSLog(@"STATUS: %i", (int)layer.status);
            }
        }else{
            NSLog(@"ignore null samplebuffer");
        }
    
    }

    相关文章

      网友评论

          本文标题:iOS 硬解H264

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