//
//  H264DecodeTool.m
//  VideoToolBoxDecodeH264
//made in zhongdao  Copyright © 2018年 AnDong. All rights reserved.
//

#import "H264DecodeTool.h"

const uint8_t lyStartCode[4] = {0, 0, 0, 1};

@interface H264DecodeTool(){
    
    //解码session
    VTDecompressionSessionRef _decoderSession;
    
    //解码format 封装了sps和pps
    CMVideoFormatDescriptionRef _decoderFormatDescription;
    
    //sps & pps
    uint8_t *_sps;
    NSInteger _spsSize;
    uint8_t *_pps;
    NSInteger _ppsSize;
    
}
@property(nonatomic,assign)BOOL isNewValue;

@end

@implementation H264DecodeTool

- (BOOL)initH264Decoder{
    
    if(_decoderSession){
        return YES;
    }

    
    const uint8_t* const parameterSetPointers[2] = { _sps, _pps };
    const size_t parameterSetSizes[2] = { _spsSize, _ppsSize };
    
    //NSLog(@"----- init h264 -- sps %@ --- pps %@", [NSData dataWithBytes:_sps length:_spsSize], [NSData dataWithBytes:_pps length:_ppsSize]);
    
    //用sps 和pps 实例化_decoderFormatDescription
    OSStatus status = CMVideoFormatDescriptionCreateFromH264ParameterSets(kCFAllocatorDefault,
                                                                          2, //参数个数
                                                                          parameterSetPointers,
                                                                          parameterSetSizes,
                                                                          4, //nal startcode开始的size
                                                                          &_decoderFormatDescription);
    
    if(status == noErr) {
        NSDictionary* destinationPixelBufferAttributes = @{
                                                           (id)kCVPixelBufferPixelFormatTypeKey : [NSNumber numberWithInt:kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange],
                                                           //硬解必须是 kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange
                                                           //                                                           或者是kCVPixelFormatType_420YpCbCr8Planar
                                                           //因为iOS是  nv12  其他是nv21
                                                           (id)kCVPixelBufferWidthKey : [NSNumber numberWithInt:1280],
                                                           (id)kCVPixelBufferHeightKey : [NSNumber numberWithInt:960],
                                                           //这里宽高和编码反的 两倍关系
                                                           (id)kCVPixelBufferOpenGLCompatibilityKey : [NSNumber numberWithBool:YES]
                                                           };

        
        
        VTDecompressionOutputCallbackRecord callBackRecord;
        callBackRecord.decompressionOutputCallback = didDecompress;
        callBackRecord.decompressionOutputRefCon = (__bridge void *)self;
        status = VTDecompressionSessionCreate(kCFAllocatorDefault,
                                              _decoderFormatDescription,
                                              NULL,
                                              (__bridge CFDictionaryRef)destinationPixelBufferAttributes,
                                              &callBackRecord,
                                              &_decoderSession);
        VTSessionSetProperty(_decoderSession, kVTDecompressionPropertyKey_ThreadCount, (__bridge CFTypeRef)[NSNumber numberWithInt:1]);
        VTSessionSetProperty(_decoderSession, kVTDecompressionPropertyKey_RealTime, kCFBooleanTrue);
    } else {
        NSLog(@"IOS8VT: reset decoder session failed status=%d", status);
        return NO;
    }
    
    return YES;
}

//解码回调
static void didDecompress( void *decompressionOutputRefCon, void *sourceFrameRefCon, OSStatus status, VTDecodeInfoFlags infoFlags, CVImageBufferRef pixelBuffer, CMTime presentationTimeStamp, CMTime presentationDuration ){
    CVPixelBufferRef *outputPixelBuffer = (CVPixelBufferRef *)sourceFrameRefCon;
    
    //持有pixelBuffer数据，否则会被释放
    *outputPixelBuffer = CVPixelBufferRetain(pixelBuffer);
    H264DecodeTool *decoder = (__bridge H264DecodeTool *)decompressionOutputRefCon;
    if (decoder.delegate)
    {
        [decoder.delegate gotDecodedFrame:pixelBuffer];
    }
}


//解码nalu裸数据
-(void)decodeNalu:(uint8_t *)frame size:(uint32_t)frameSize alive:(BOOL)isAlive
{
    
    int flag = 1;
    uint8_t * packetBuffer = NULL;
    long packetSize = 0;
    
    long count = 0, location = 0;
    for (long i = 0 ; i < frameSize; i++) {
        if (frame[i] == 0) {
            count++;
        } else if (frame[i] == 1 && ((isAlive && count == 3) || ( !isAlive && count >= 3)) && i > 3) {
            // 0x00 0x00 0x00 0x01  如果存在多个0x00 以前的count == 3 就有问题 所以用count >= 3 正确,这里专门针对DQ001和除它之外的其他设备的实时和回看适配
            if (packetBuffer) {
                free(packetBuffer);
                packetBuffer = NULL;
            }
            packetSize = i - location - 3;
            
            packetBuffer = (uint8_t *)malloc(packetSize);
            memcpy(packetBuffer, frame+location, packetSize);
            
            location = i - 3;
            count = 0;
            
            flag = 0;
            //NSLog(@"1---%@", [NSData dataWithBytes:packetBuffer length:packetSize]);
            [self oneDecodeNalu:packetBuffer size:(uint32_t)packetSize];
        } else {
            count = 0;
        }
    }
    
    
    if (flag) {
        //NSLog(@"2---%@", [NSData dataWithBytes:frame length:frameSize]);
        [self oneDecodeNalu:frame size:frameSize];
    } else {
        free(packetBuffer);
        packetBuffer = NULL;
        packetSize = frameSize - location;
        
        packetBuffer = (uint8_t *)malloc(packetSize);
        memcpy(packetBuffer, frame+location, packetSize);
        
        //NSLog(@"3---%@", [NSData dataWithBytes:packetBuffer length:packetSize]);
        [self oneDecodeNalu:packetBuffer size:(uint32_t)packetSize];
    }
    
    //NSLog(@"4---");
    
    
}


-(void)oneDecodeNalu:(uint8_t *)frame size:(uint32_t)frameSize {
    //    NSLog(@"------------开始解码");
    
    //获取nalu type
    int nalu_type = (frame[4] & 0x1F);
    CVPixelBufferRef pixelBuffer = NULL;
    
    //填充nalu size 去掉start code 替换成nalu size
    uint32_t nalSize = (uint32_t)(frameSize - 4);
    uint8_t *pNalSize = (uint8_t*)(&nalSize);
    frame[0] = *(pNalSize + 3);
    frame[1] = *(pNalSize + 2);
    frame[2] = *(pNalSize + 1);
    frame[3] = *(pNalSize);
  
    switch (nalu_type)
    {
        case 0x05:
            //关键帧
            if([self initH264Decoder])
            {
                pixelBuffer = [self decode:frame size:frameSize];
            }
            break;
        case 0x07:
            //sps
            _spsSize = frameSize - 4;
//            uint8_t *oldsps = _sps;
      
            _sps = (uint8_t *)malloc(_spsSize);
            memcpy(_sps, &frame[4], _spsSize);
//            if (oldsps != _sps) {
//                self.isNewValue = YES;
//            }
            break;
        case 0x08:
        {
            //pps
//            uint8_t * oldpps = _pps;
            
            _ppsSize = frameSize - 4;
            _pps = (uint8_t *)malloc(_ppsSize);
            memcpy(_pps, &frame[4], _ppsSize);
            
//            if (oldpps != _pps) {
//                self.isNewValue = YES;
//            }
            break;
        }
        default:
        {
            // B/P frame
            if([self initH264Decoder])
            {
                pixelBuffer = [self decode:frame size:frameSize];
            }
            break;
        }
            
            
    }
}


//解码帧数据
- (CVPixelBufferRef)decode:(uint8_t *)frame size:(uint32_t)frameSize{
    CVPixelBufferRef outputPixelBuffer = NULL;
    
    CMBlockBufferRef blockBuffer = NULL;
    
    //创建CMBlockBufferRef
    OSStatus status = CMBlockBufferCreateWithMemoryBlock(NULL,
                                                         (void *)frame,
                                                         frameSize,
                                                         kCFAllocatorNull,
                                                         NULL,
                                                         0,
                                                         frameSize,
                                                         FALSE,
                                                         &blockBuffer);
    if (status == kCMBlockBufferNoErr) {
        
        CMSampleBufferRef sampleBuffer = NULL;
        const size_t sampleSizeArray[] = {frameSize};
        
        //创建sampleBuffer
        status = CMSampleBufferCreateReady(kCFAllocatorDefault,
                                           blockBuffer,
                                           _decoderFormatDescription ,
                                           1, 0, NULL, 1, sampleSizeArray,
                                           &sampleBuffer);
        
        if (status == kCMBlockBufferNoErr && sampleBuffer) {
            VTDecodeFrameFlags flags = 0;
            VTDecodeInfoFlags flagOut = 0;
            //CMSampleBufferRef丢进去解码
            OSStatus decodeStatus = VTDecompressionSessionDecodeFrame(_decoderSession,
                                                                      sampleBuffer,
                                                                      flags,
                                                                      &outputPixelBuffer,
                                                                      &flagOut);
            
            if(decodeStatus == kVTInvalidSessionErr) {
                NSLog(@"IOS8VT: Invalid session, reset decoder session");
            } else if(decodeStatus == kVTVideoDecoderBadDataErr) {
                NSLog(@"IOS8VT: decode failed status=%d(Bad data)", decodeStatus);
            } else if(decodeStatus != noErr) {
                NSLog(@"IOS8VT: decode failed status=%d", decodeStatus);
            }
            CFRelease(sampleBuffer);
        }
         CFRelease(blockBuffer);
    }
    //返回pixelBuffer数据
    return outputPixelBuffer;
}

- (void)endDecode{
    
    if(_decoderSession) {
        VTDecompressionSessionInvalidate(_decoderSession);
        CFRelease(_decoderSession);
        _decoderSession = NULL;
    }
    
    if(_decoderFormatDescription) {
        CFRelease(_decoderFormatDescription);
        _decoderFormatDescription = NULL;
    }
    
    if (_sps) {
        free(_sps);
    }
    
    if (_pps) {
        free(_pps);
    }

    _ppsSize = _spsSize = 0;
}



@end
