123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626 |
- //
- // BFVideoDecoder.m
- // BFFFmpeglib_Example
- //
- // Created by ak on 2022/1/5.
- // Copyright © 2022 287971051@qq.com. All rights reserved.
- //
- #import "BFVideoDecoder.h"
- #import <VideoToolbox/VideoToolbox.h>
- #import <pthread.h>
- #include "BFMediaLog.h"
- typedef struct {
- CVPixelBufferRef outputPixelbuffer;
- int rotate;
- Float64 pts;
- int fps;
- int source_index;
- } BFDecodeVideoInfo;
- typedef struct {
- uint8_t *vps;
- uint8_t *sps;
-
- // H265有前后两个pps
- uint8_t *f_pps;
- uint8_t *r_pps;
-
- int vps_size;
- int sps_size;
- int f_pps_size;
- int r_pps_size;
-
- Float64 last_decode_pts;
- } BFDecoderInfo;
- @interface BFVideoDecoder ()
- {
- VTDecompressionSessionRef _decoderSession;
- CMVideoFormatDescriptionRef _decoderFormatDescription;
- BFDecoderInfo _decoderInfo;
- pthread_mutex_t _decoder_lock;
-
- uint8_t *_lastExtraData;
- int _lastExtraDataSize;
-
- BOOL _isFirstFrame;
- }
- @end
- @implementation BFVideoDecoder
- #pragma mark - 解码后回调
- /// @param decompressionOutputRefCon 回调引用
- /// @param sourceFrameRefCon 帧引用
- /// @param status 状态标识
- /// @param infoFlags 同步还是异步解码
- /// @param pixelBuffer 实际的图像缓存
- /// @param presentationTimeStamp 图像出现的时间戳
- /// @param presentationDuration 图像的持续时间
- static void VideoDecoderCallback(void *decompressionOutputRefCon, void *sourceFrameRefCon, OSStatus status, VTDecodeInfoFlags infoFlags, CVImageBufferRef pixelBuffer, CMTime presentationTimeStamp, CMTime presentationDuration) {
- BFDecodeVideoInfo *sourceRef = (BFDecodeVideoInfo *)sourceFrameRefCon;
- NSLog(@"2827解码pts=%f 完成时间为: %f",sourceRef->pts,[[NSDate date] timeIntervalSince1970]);
- if (pixelBuffer == NULL || status != noErr) {
- NSLog(@" pixelbuffer is NULL status = %d",status);
- if (sourceRef) {
- free(sourceRef);
- }
- return;
- }
-
- //取到 OC 本类对象
- BFVideoDecoder *decoder = (__bridge BFVideoDecoder *)decompressionOutputRefCon;
- CMSampleTimingInfo sampleTime = {
- .presentationTimeStamp = presentationTimeStamp,
- .decodeTimeStamp = presentationTimeStamp
- };
-
- //pixelbuffer to samplerbuffer
- CMSampleBufferRef samplebuffer = [decoder createSampleBufferFromPixelbuffer:pixelBuffer
- videoRotate:sourceRef->rotate
- timingInfo:sampleTime];
-
- if (samplebuffer) {
- if ([decoder.delegate respondsToSelector:@selector(getVideoDecodeDataCallback:isFirstFrame:)]) {
- [decoder.delegate getVideoDecodeDataCallback:samplebuffer isFirstFrame:decoder->_isFirstFrame];
- if (decoder->_isFirstFrame) {
- decoder->_isFirstFrame = NO;
- }
- }
- CFRelease(samplebuffer);
- }
-
- if (sourceRef) {
- free(sourceRef);
- }
- }
- #pragma mark - life cycle
- - (instancetype)init {
- if (self = [super init]) {
- _decoderInfo = {
- .vps = NULL, .sps = NULL, .f_pps = NULL, .r_pps = NULL,
- .vps_size = 0, .sps_size = 0, .f_pps_size = 0, .r_pps_size = 0, .last_decode_pts = 0,
- };
- _isFirstFrame = YES;
- pthread_mutex_init(&_decoder_lock, NULL);
- }
- return self;
- }
- - (void)dealloc {
- _delegate = nil;
- [self destoryDecoder];
- }
- #pragma mark - Public
- - (void)startDecodeVideoData:(BFParseVideoDataInfo *)videoInfo {
- // get extra data
- if (videoInfo->extraData && videoInfo->extraDataSize) {
- uint8_t *extraData = videoInfo->extraData;
- int size = videoInfo->extraDataSize;
-
- BOOL isNeedUpdate = [self isNeedUpdateExtraDataWithNewExtraData:extraData
- newSize:size
- lastData:&_lastExtraData
- lastSize:&_lastExtraDataSize];
- if (isNeedUpdate) {
- NSLog(@" update extra data");
-
- [self getNALUInfoWithVideoFormat:videoInfo->videoFormat
- extraData:extraData
- extraDataSize:size
- decoderInfo:&_decoderInfo];
- }
-
- }
-
- // 创建解码器
- if (!_decoderSession) {
- _decoderSession = [self createDecoderWithVideoInfo:videoInfo
- videoDescRef:&_decoderFormatDescription
- videoFormat:kCVPixelFormatType_420YpCbCr8BiPlanarFullRange
- lock:_decoder_lock
- callback:VideoDecoderCallback
- decoderInfo:_decoderInfo];
- }
-
- pthread_mutex_lock(&_decoder_lock);
- if (!_decoderSession) {
- pthread_mutex_unlock(&_decoder_lock);
- return;
- }
-
- /* If open B frame, the code will not be used.
- if(_decoderInfo.last_decode_pts != 0 && videoInfo->pts <= _decoderInfo.last_decode_pts){
- log4cplus_error(kModuleName, "decode timestamp error ! current:%f, last:%f",videoInfo->pts, _decoderInfo.last_decode_pts);
- pthread_mutex_unlock(&_decoder_lock);
- return;
- }
- */
-
- _decoderInfo.last_decode_pts = videoInfo->pts;
-
- pthread_mutex_unlock(&_decoder_lock);
-
- // start decode
- [self startDecode:videoInfo
- session:_decoderSession
- lock:_decoder_lock];
- }
- - (void)stopDecoder {
- [self destoryDecoder];
- }
- #pragma mark - private methods
- #pragma mark Create / Destory decoder
- - (VTDecompressionSessionRef)createDecoderWithVideoInfo:(BFParseVideoDataInfo *)videoInfo videoDescRef:(CMVideoFormatDescriptionRef *)videoDescRef videoFormat:(OSType)videoFormat lock:(pthread_mutex_t)lock callback:(VTDecompressionOutputCallback)callback decoderInfo:(BFDecoderInfo)decoderInfo {
- pthread_mutex_lock(&lock);
-
- // 1.根据 sps,pps 设置解码参数 区分264 265
- OSStatus status;
- // H264 格式
- if (videoInfo->videoFormat == BFH264EncodeFormat) {
- // 存放 sps 和 pps 信息的数组
- const uint8_t *const parameterSetPointers[2] = {decoderInfo.sps, decoderInfo.f_pps};
-
- const size_t parameterSetSizes[2] = {static_cast<size_t>(decoderInfo.sps_size), static_cast<size_t>(decoderInfo.f_pps_size)};
- /*
- 参数 1: allocator 内存分配器, 使用默认的 KCFAllocatorDefault
- 参数 2: parameterSetCount 解码参数的个数 2 (分别为 sps, pps)
- 参数 3: parameterSetPointers 参数集的地址
- 参数 4: parameterSetSizes 参数集的大小
- 参数 5: NALUnitHeaderLength 流数据起始位的长度 大端模式的起始位的长度固定为 4
- 参数 6: formatDescriptionOut 解码器的描述
- */
- status = CMVideoFormatDescriptionCreateFromH264ParameterSets(kCFAllocatorDefault,
- 2,
- parameterSetPointers,
- parameterSetSizes,
- 4,
- videoDescRef);
- // H265 格式
- }else if (videoInfo->videoFormat == BFH265EncodeFormat) {
- //有 PPS
- if (decoderInfo.r_pps_size == 0) {
- const uint8_t *const parameterSetPointers[3] = {decoderInfo.vps, decoderInfo.sps, decoderInfo.f_pps};
- const size_t parameterSetSizes[3] = {static_cast<size_t>(decoderInfo.vps_size), static_cast<size_t>(decoderInfo.sps_size), static_cast<size_t>(decoderInfo.f_pps_size)};
- if (@available(iOS 11.0, *)) {
- status = CMVideoFormatDescriptionCreateFromHEVCParameterSets(kCFAllocatorDefault,
- 3,
- parameterSetPointers,
- parameterSetSizes,
- 4,
- NULL,
- videoDescRef);
- } else {
- status = -1;
- NSLog(@" System version is too low!");
- }
- } else {
- const uint8_t *const parameterSetPointers[4] = {decoderInfo.vps, decoderInfo.sps, decoderInfo.f_pps, decoderInfo.r_pps};
- const size_t parameterSetSizes[4] = {static_cast<size_t>(decoderInfo.vps_size), static_cast<size_t>(decoderInfo.sps_size), static_cast<size_t>(decoderInfo.f_pps_size), static_cast<size_t>(decoderInfo.r_pps_size)};
- if (@available(iOS 11.0, *)) {
- status = CMVideoFormatDescriptionCreateFromHEVCParameterSets(kCFAllocatorDefault,
- 4,
- parameterSetPointers,
- parameterSetSizes,
- 4,
- NULL,
- videoDescRef);
- } else {
- status = -1;
- NSLog(@" System version is too low!");
- }
- }
- }else {
- status = -1;
- }
-
- if (status != noErr) {
- NSLog(@" NALU header error !");
- pthread_mutex_unlock(&lock);
- [self destoryDecoder];
- return NULL;
- }
- // 2.解码参数
- uint32_t pixelFormatType = videoFormat;
- const void *keys[] = {kCVPixelBufferPixelFormatTypeKey};
- const void *values[] = {CFNumberCreate(NULL, kCFNumberSInt32Type, &pixelFormatType)};
- CFDictionaryRef attrs = CFDictionaryCreate(NULL, keys, values, 1, NULL, NULL);
-
- //解码完成后的回调
- VTDecompressionOutputCallbackRecord callBackRecord;
- callBackRecord.decompressionOutputCallback = callback;
- callBackRecord.decompressionOutputRefCon = (__bridge void *)self;
-
-
- //3,创建 session
- VTDecompressionSessionRef session;
- /*
- 参数 1: allocator 内存分配器, 使用默认的 kCFAllocatorDefault
- 参数 2: videoFormatDescription 描述源视频帧的视频格式 _decodeDesc
- 参数 3: videoDecoderSpecification 是否需要特定的视频解码器, 不需要给 NULL 即可
- 参数 4: destinationImageBufferAttributes 描述源像素的缓存区,如果没特殊要求给 NULL 即可, 有的话就把设置的字典信息填进去
- 参数 5: outputCallback 已经解码完成的回调函数
- 参数 6: decompressionSessionOut 解压 session
- */
- status = VTDecompressionSessionCreate(kCFAllocatorDefault,
- *videoDescRef,
- NULL,
- attrs,
- &callBackRecord,
- &session);
-
- CFRelease(attrs);
- pthread_mutex_unlock(&lock);
- if (status != noErr) {
- NSLog(@" Create decoder failed");
- [self destoryDecoder];
- return NULL;
- }
-
- // 4. 设置解码器是实时解码,
- // status = VTSessionSetProperty(_decodeSession, kVTDecompressionPropertyKey_RealTime, kCFBooleanTrue);
- // if (status != noErr) {
- // NSLog(@"initDecoder VTSessionSetProperty() failed! ");
- // }
-
- NSLog(@"video decoder init success!");
-
-
- return session;
- }
- - (void)destoryDecoder {
- pthread_mutex_lock(&_decoder_lock);
-
- if (_decoderInfo.vps) {
- free(_decoderInfo.vps);
- _decoderInfo.vps_size = 0;
- _decoderInfo.vps = NULL;
- }
-
- if (_decoderInfo.sps) {
- free(_decoderInfo.sps);
- _decoderInfo.sps_size = 0;
- _decoderInfo.sps = NULL;
- }
-
- if (_decoderInfo.f_pps) {
- free(_decoderInfo.f_pps);
- _decoderInfo.f_pps_size = 0;
- _decoderInfo.f_pps = NULL;
- }
-
- if (_decoderInfo.r_pps) {
- free(_decoderInfo.r_pps);
- _decoderInfo.r_pps_size = 0;
- _decoderInfo.r_pps = NULL;
- }
-
- if (_lastExtraData) {
- free(_lastExtraData);
- _lastExtraDataSize = 0;
- _lastExtraData = NULL;
- }
-
- if (_decoderSession) {
- //等待异步解码完成
- VTDecompressionSessionWaitForAsynchronousFrames(_decoderSession);
- // 设置会话无效s
- VTDecompressionSessionInvalidate(_decoderSession);
- CFRelease(_decoderSession);
- _decoderSession = NULL;
- }
- // 清空解码的视频格式描述信息
- if (_decoderFormatDescription) {
- CFRelease(_decoderFormatDescription);
- _decoderFormatDescription = NULL;
- }
- pthread_mutex_unlock(&_decoder_lock);
- }
- - (BOOL)isNeedUpdateExtraDataWithNewExtraData:(uint8_t *)newData newSize:(int)newSize lastData:(uint8_t **)lastData lastSize:(int *)lastSize {
- BOOL isNeedUpdate = NO;
- if (*lastSize == 0) {
- isNeedUpdate = YES;
- }else {
- if (*lastSize != newSize) {
- isNeedUpdate = YES;
- }else {
- if (memcmp(newData, *lastData, newSize) != 0) {
- isNeedUpdate = YES;
- }
- }
- }
-
- if (isNeedUpdate) {
- [self destoryDecoder];
-
- *lastData = (uint8_t *)malloc(newSize);
- memcpy(*lastData, newData, newSize);
- *lastSize = newSize;
- }
-
- return isNeedUpdate;
- }
- #pragma mark Parse NALU Header
- - (void)copyDataWithOriginDataRef:(uint8_t **)originDataRef newData:(uint8_t *)newData size:(int)size {
- if (*originDataRef) {
- free(*originDataRef);
- *originDataRef = NULL;
- }
- *originDataRef = (uint8_t *)malloc(size);
- memcpy(*originDataRef, newData, size);
- }
- - (void)getNALUInfoWithVideoFormat:(BFVideoEncodeFormat)videoFormat extraData:(uint8_t *)extraData extraDataSize:(int)extraDataSize decoderInfo:(BFDecoderInfo *)decoderInfo {
- uint8_t *data = extraData;
- int size = extraDataSize;
-
- int startCodeVPSIndex = 0;
- int startCodeSPSIndex = 0;
- int startCodeFPPSIndex = 0;
- int startCodeRPPSIndex = 0;
- int nalu_type = 0;
-
- for (int i = 0; i < size; i ++) {
- if (i >= 3) {
- if (data[i] == 0x01 && data[i - 1] == 0x00 && data[i - 2] == 0x00 && data[i - 3] == 0x00) {
- if (videoFormat == BFH264EncodeFormat) {
- if (startCodeSPSIndex == 0) {
- startCodeSPSIndex = i;
- }
- if (i > startCodeSPSIndex) {
- startCodeFPPSIndex = i;
- }
- }else if (videoFormat == BFH265EncodeFormat) {
- if (startCodeVPSIndex == 0) {
- startCodeVPSIndex = i;
- continue;
- }
- if (i > startCodeVPSIndex && startCodeSPSIndex == 0) {
- startCodeSPSIndex = i;
- continue;
- }
- if (i > startCodeSPSIndex && startCodeFPPSIndex == 0) {
- startCodeFPPSIndex = i;
- continue;
- }
- if (i > startCodeFPPSIndex && startCodeRPPSIndex == 0) {
- startCodeRPPSIndex = i;
- }
- }
- }
- }
- }
-
- int spsSize = startCodeFPPSIndex - startCodeSPSIndex - 4;
- decoderInfo->sps_size = spsSize;
-
- if (videoFormat == BFH264EncodeFormat) {
- int f_ppsSize = size - (startCodeFPPSIndex + 1);
- decoderInfo->f_pps_size = f_ppsSize;
-
- nalu_type = ((uint8_t)data[startCodeSPSIndex + 1] & 0x1F);
- if (nalu_type == 0x07) {
- uint8_t *sps = &data[startCodeSPSIndex + 1];
- [self copyDataWithOriginDataRef:&decoderInfo->sps newData:sps size:spsSize];
- }
-
- nalu_type = ((uint8_t)data[startCodeFPPSIndex + 1] & 0x1F);
- if (nalu_type == 0x08) {
- uint8_t *pps = &data[startCodeFPPSIndex + 1];
- [self copyDataWithOriginDataRef:&decoderInfo->f_pps newData:pps size:f_ppsSize];
- }
- } else {
- int vpsSize = startCodeSPSIndex - startCodeVPSIndex - 4;
- decoderInfo->vps_size = vpsSize;
-
- int f_ppsSize = startCodeRPPSIndex - startCodeFPPSIndex - 4;
- decoderInfo->f_pps_size = f_ppsSize;
-
- nalu_type = ((uint8_t) data[startCodeVPSIndex + 1] & 0x4F);
- if (nalu_type == 0x40) {
- uint8_t *vps = &data[startCodeVPSIndex + 1];
- [self copyDataWithOriginDataRef:&decoderInfo->vps newData:vps size:vpsSize];
- }
-
- nalu_type = ((uint8_t) data[startCodeSPSIndex + 1] & 0x4F);
- if (nalu_type == 0x42) {
- uint8_t *sps = &data[startCodeSPSIndex + 1];
- [self copyDataWithOriginDataRef:&decoderInfo->sps newData:sps size:spsSize];
- }
-
- nalu_type = ((uint8_t) data[startCodeFPPSIndex + 1] & 0x4F);
- if (nalu_type == 0x44) {
- uint8_t *pps = &data[startCodeFPPSIndex + 1];
- [self copyDataWithOriginDataRef:&decoderInfo->f_pps newData:pps size:f_ppsSize];
- }
-
- if (startCodeRPPSIndex == 0) {
- return;
- }
-
- int r_ppsSize = size - (startCodeRPPSIndex + 1);
- decoderInfo->r_pps_size = r_ppsSize;
-
- nalu_type = ((uint8_t) data[startCodeRPPSIndex + 1] & 0x4F);
- if (nalu_type == 0x44) {
- uint8_t *pps = &data[startCodeRPPSIndex + 1];
- [self copyDataWithOriginDataRef:&decoderInfo->r_pps newData:pps size:r_ppsSize];
- }
- }
- }
- #pragma mark 开始解码
- - (void)startDecode:(BFParseVideoDataInfo *)videoInfo session:(VTDecompressionSessionRef)session lock:(pthread_mutex_t)lock {
- pthread_mutex_lock(&lock);
- uint8_t *data = videoInfo->data;
- int size = videoInfo->dataSize;
- int rotate = videoInfo->videoRotate;
- CMSampleTimingInfo timingInfo = videoInfo->timingInfo;
-
- uint8_t *tempData = (uint8_t *)malloc(size);
- memcpy(tempData, data, size);
-
- BFDecodeVideoInfo *sourceRef = (BFDecodeVideoInfo *)malloc(sizeof(BFParseVideoDataInfo));
- sourceRef->outputPixelbuffer = NULL;
- sourceRef->rotate = rotate;
- sourceRef->pts = videoInfo->pts;
- sourceRef->fps = videoInfo->fps;
-
- NSLog(@"2827开始解码pts=%f 时间为: %f", videoInfo->pts,[[NSDate date] timeIntervalSince1970]);
- //111.MP4
-
- // 1.创建 blockBuffer
- /*
- 参数 1: structureAllocator 内存分配器
- 参数 2: memoryBlock 内容,也就是帧,frame
- 参数 3: blockLength 内容的长度
- 参数 4: blockAllocator 内存分配器. 给 NULL/kCFAllocatorNull 即可
- 参数 5: customBlockSource NULL
- 参数 6: offsetToData 数据偏移量, 没有的话就给 0, 从头开始读取
- 参数 7: dataLength 数据长度
- 参数 8: flags 给 0 即可
- 参数 9: blockBufferOut blockBuffer 的地址
- */
- CMBlockBufferRef blockBuffer;
- OSStatus status = CMBlockBufferCreateWithMemoryBlock(kCFAllocatorDefault,
- (void *)tempData,
- size, kCFAllocatorNull,
- NULL,
- 0,
- size,
- 0,
- &blockBuffer);
-
- if (status == kCMBlockBufferNoErr) {
-
- // 2. 创建 sampleBuffer
- CMSampleBufferRef sampleBuffer = NULL;
- const size_t sampleSizeArray[] = { static_cast<size_t>(size) };
- /*
- 参数1: allocator 分配器,使用默认内存分配, kCFAllocatorDefault
- 参数2: blockBuffer.需要编码的数据blockBuffer.不能为NULL
- 参数3: formatDescription,视频输出格式
- 参数4: numSamples.CMSampleBuffer 个数.
- 参数5: numSampleTimingEntries 必须为0,1,numSamples
- 参数6: sampleTimingArray. 数组.为空
- 参数7: numSampleSizeEntries 默认为1
- 参数8: sampleSizeArray
- 参数9: sampleBuffer对象
- */
- status = CMSampleBufferCreateReady(kCFAllocatorDefault,
- blockBuffer,
- _decoderFormatDescription,
- 1,
- 1,
- &timingInfo,
- 1,
- sampleSizeArray,
- &sampleBuffer);
-
- if (status == kCMBlockBufferNoErr && sampleBuffer) {
- // 3.调用解码函数
- //解码模式
- VTDecodeFrameFlags flags = kVTDecodeFrame_EnableAsynchronousDecompression;
- //异步解码
- VTDecodeInfoFlags flagOut = kVTDecodeInfo_Asynchronous;
- /*
- VTDecompressionSessionDecodeFrame(
- VTDecompressionSessionRef session, //解码器 Session
- CMSampleBufferRef sampleBuffer, // 源数据 包含一个或多个视频帧的CMsampleBuffer
- VTDecodeFrameFlags decodeFlags, // 解码标志位。bit 0 is enableAsynchronousDecompression
- void * sourceFrameRefCon, //用户自定义参数指针。
- VTDecodeInfoFlags * infoFlagsOut //解码输出标志
- )
- */
- OSStatus decodeStatus = VTDecompressionSessionDecodeFrame(session,
- sampleBuffer,
- flags,
- sourceRef,
- &flagOut);
- NSLog(@"decodeStatus is:%d",(int)decodeStatus);
- if(decodeStatus == kVTInvalidSessionErr) {
- pthread_mutex_unlock(&lock);
- [self destoryDecoder];
- if (blockBuffer)
- CFRelease(blockBuffer);
- free(tempData);
- tempData = NULL;
- CFRelease(sampleBuffer);
- return;
-
- }
- CFRelease(sampleBuffer);
- }
- }
-
- if (blockBuffer) {
- CFRelease(blockBuffer);
- }
-
- free(tempData);
- tempData = NULL;
- pthread_mutex_unlock(&lock);
- }
- #pragma mark - Other
- - (CMSampleBufferRef)createSampleBufferFromPixelbuffer:(CVImageBufferRef)pixelBuffer videoRotate:(int)videoRotate timingInfo:(CMSampleTimingInfo)timingInfo {
- if (!pixelBuffer) {
- return NULL;
- }
-
- CVPixelBufferRef final_pixelbuffer = pixelBuffer;
- CMSampleBufferRef samplebuffer = NULL;
- CMVideoFormatDescriptionRef videoInfo = NULL;
- OSStatus status = CMVideoFormatDescriptionCreateForImageBuffer(kCFAllocatorDefault, final_pixelbuffer, &videoInfo);
- status = CMSampleBufferCreateForImageBuffer(kCFAllocatorDefault, final_pixelbuffer, true, NULL, NULL, videoInfo, &timingInfo, &samplebuffer);
-
- if (videoInfo != NULL) {
- CFRelease(videoInfo);
- }
-
- if (samplebuffer == NULL || status != noErr) {
- return NULL;
- }
-
- return samplebuffer;
- }
- - (void)resetTimestamp {
- _decoderInfo.last_decode_pts = 0;
- }
- @end
|