VTDecompressionSessionInvalidate线程卡死挂起导致的解码问题

Decode Error

VideoToolBox解码时,VTDecompressionSessionDecodeFrame返回错误12902,kVTParameterErr = -12902
即参数错误,然后在VTDecompressionSessionInvalidate的时候,就发现线程卡死挂起了
同样操作VTDecompressionSessionWaitForAsynchronousFrames也是一样的,VTDecompressionSessionInvalidate里面也操作了VTDecompressionSessionWaitForAsynchronousFrames

CMItemCount count = CMSampleBufferGetNumSamples(sampleBuffer);
if (count > 0) {
    //解码
    //向视频解码器提示使用低功耗模式是可以的
    uint32_t decoder_flags          = 0;
    decoder_flags |= kVTDecodeFrame_EnableAsynchronousDecompression;
    decoder_flags |= kVTDecodeFrame_1xRealTimePlayback;
    //异步解码
    VTDecodeInfoFlags  flagOut = kVTDecodeInfo_Asynchronous;
    NSDate* currentTime = [NSDate date];
    status = VTDecompressionSessionDecodeFrame(_decompressionSession, sampleBuffer, decoder_flags,
                                               (void*)CFBridgingRetain(currentTime), &flagOut);
    if (status == kVTInvalidSessionErr) {
        GSLog(@"Video hard decode  InvalidSessionErr status = %d ,nalutype : %d", (int)status,nalu_type);
    } else if (status == kVTVideoDecoderBadDataErr) {
        GSLog(@"Video hard decode  BadData status = %d ,nalutype : %d", (int)status,nalu_type);
    } else if (status != noErr) {
        GSLog(@"Video hard decode failed status = %d , nalutype : %d", (int)status,nalu_type);
    }
    CFBridgingRelease((__bridge CFTypeRef _Nullable)(currentTime));
}else {
    GSLog(@"sampleBuffer items == 0");
}

然后释放的时候就卡死了

VTDecompressionSessionInvalidate(oldsession);
CFRelease(oldsession);

这里附上IJKPlayer的写法

if (context->vt_session) {
        VTDecompressionSessionWaitForAsynchronousFrames(context->vt_session);
        VTDecompressionSessionInvalidate(context->vt_session);
        CFRelease(context->vt_session);
        context->vt_session = NULL;
    }

这里防止卡死我把它放在另一个线程去做了

if (self->_decompressionSession) {
        VTDecompressionSessionRef oldsession = self->_decompressionSession;
        dispatch_async(dispatch_get_global_queue(0, 0), ^{
            //为防止sesssion在invalidate卡死线程的casem,我们在另外一个线程去执行,就算卡死也不会影响我们的解码线程或者主线程
            NSLog(@"Video Decompression Invalidate session BEGIN");
            VTDecompressionSessionInvalidate(oldsession);
            CFRelease(oldsession);
            NSLog(@"Video Decompression Invalidate session DONE");
        });
        self->_decompressionSession = NULL;
    }

这是相关的堆栈,拷贝的别人的

Apple could fix the bug of VTDecompressionSessionInvalidate API. Here is the stack:

0   libsystem_kernel.dylib         0x0000000184905150 __psynch_cvwait + 8

1   libsystem_pthread.dylib       0x0000000184a1ad40 _pthread_cond_wait$VARIANT$mp + 640

2   CoreMedia                     0x0000000187ec44f8 WaitOnCondition + 16

3   CoreMedia                     0x0000000187ec4440 FigSemaphoreWaitRelative + 168

4   ************                   0x000000018840b0d8 VTDecompressionSessionRemote_WaitForAsynchronousFrames + 120

5   ************                   0x000000018840aeb8 VTDecompressionSessionRemote_Invalidate + 88

6   ************                   0x00000001883b1e44 VTDecompressionSessionInvalidate + 52

7   XXXXX                       0x0000000102a21094 -[hw264decoder releasedecoder] + 3641492 (XXXXXX.m:221)

追根溯源发现收到的naluType类型为3131: Unspecified (non-VCL)是不是还没用的类型吗,我也不知道怎么会收到这个,还是数据源错误,打包成CMSampleBufferVideoToolBox也就出问题了,在接收到nalutype的时候,过滤掉了31类型,解决了这个bug,如果你也有遇到这种问题,看看是不是nalutype的问题吧,或者数据源不对。

发现使用AVSampleBufferDisplayerlayer的时候,也有同样的现象

CFArrayRef attachments = CMSampleBufferGetSampleAttachmentsArray(sampleBuffer, YES);
CFMutableDictionaryRef dict = (CFMutableDictionaryRef)CFArrayGetValueAtIndex(attachments, 0);
CFDictionarySetValue(dict, kCMSampleAttachmentKey_DisplayImmediately, kCFBooleanTrue);

错误枚举

// Error codes
#if COREMEDIA_USE_DERIVED_ENUMS_FOR_CONSTANTS
enum : OSStatus
#else
enum
#endif // COREMEDIA_USE_DERIVED_ENUMS_FOR_CONSTANTS
{
	kVTPropertyNotSupportedErr				= -12900,
	kVTPropertyReadOnlyErr					= -12901,
	kVTParameterErr							= -12902,
	kVTInvalidSessionErr					= -12903,
	kVTAllocationFailedErr					= -12904,
	kVTPixelTransferNotSupportedErr			= -12905, // c.f. -8961
	kVTCouldNotFindVideoDecoderErr			= -12906,
	kVTCouldNotCreateInstanceErr			= -12907,
	kVTCouldNotFindVideoEncoderErr			= -12908,
	kVTVideoDecoderBadDataErr				= -12909, // c.f. -8969
	kVTVideoDecoderUnsupportedDataFormatErr	= -12910, // c.f. -8970
	kVTVideoDecoderMalfunctionErr			= -12911, // c.f. -8960
	kVTVideoEncoderMalfunctionErr			= -12912,
	kVTVideoDecoderNotAvailableNowErr		= -12913,
	kVTImageRotationNotSupportedErr			= -12914,
	kVTVideoEncoderNotAvailableNowErr		= -12915,
	kVTFormatDescriptionChangeNotSupportedErr	= -12916,
	kVTInsufficientSourceColorDataErr		= -12917,
	kVTCouldNotCreateColorCorrectionDataErr	= -12918,
	kVTColorSyncTransformConvertFailedErr	= -12919,
	kVTVideoDecoderAuthorizationErr			= -12210,
	kVTVideoEncoderAuthorizationErr			= -12211,
	kVTColorCorrectionPixelTransferFailedErr	= -12212,
	kVTMultiPassStorageIdentifierMismatchErr	= -12213,
	kVTMultiPassStorageInvalidErr			= -12214,
	kVTFrameSiloInvalidTimeStampErr			= -12215,
	kVTFrameSiloInvalidTimeRangeErr			= -12216,
	kVTCouldNotFindTemporalFilterErr		= -12217,
	kVTPixelTransferNotPermittedErr			= -12218,
	kVTColorCorrectionImageRotationFailedErr	= -12219,
	kVTVideoDecoderRemovedErr				= -17690,
};

相关文章:

  1. VTDecompressionSessionInvalidate hangs on iOS 11
    大概意思就是传入的CMSampleBuffer里面的数量为空,他判断下数量不为空再传入。即上面CMItemCount count = CMSampleBufferGetNumSamples(sampleBuffer);的代码
  2. VTDecompressionSessionDecodeFrame hangs during applicationDidEnterBackground
    再者就是VTDecompressionSessionInvalidate放在另一个线程,不卡死当前线程,也是治标不治本

学习不断,有错误请指出,如果对你有帮助,请点个赞

你可能感兴趣的:(音视频开发)