iOS 硬解H264

公司摄像机产品客户端需要添加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");
    }

}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,457评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,837评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,696评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,183评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,057评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,105评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,520评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,211评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,482评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,574评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,353评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,213评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,576评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,897评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,489评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,683评论 2 335

推荐阅读更多精彩内容