- (void)encodeSampleBuffer:(CMSampleBufferRef)buffer; // 编码。 在这个 Demo 里我们通过 CMSampleBufferRef 打包的是编码后的 AAC 数据,将其作为输入送给封装模块。 CMSampleBufferRef audioHeader = (CMSampleBufferRef)CMSimpleQueueGetHead(_audioQueue); CMSampleBufferRef audioFirstBuffer = (CMSampleBufferRef)CMSimpleQueueGetHead(_audioQueue); CMSampleBufferRef while (CMSimpleQueueGetCount(_audioQueue) > 0) { CMSampleBufferRef sampleBuffer = (CMSampleBufferRef
在上面的拷贝下一份音频/视频采样数据接口中,我们使用的是依然 CMSampleBufferRef[1] 作为返回值类型。 在这个接口中我们通过 CMSampleBufferRef 打包的是从 MP4/M4A 文件解封装后得到的 AAC 编码数据。 CMSampleBufferRef sampleBuffer = NULL; while (! CMSampleBufferRef sampleBuffer = NULL; while (! 参考资料 [1] CMSampleBufferRef: https://developer.apple.com/documentation/coremedia/cmsamplebufferref/ -
- (void)decodeSampleBuffer:(CMSampleBufferRef)sampleBuffer; // 解码。 在上面的解码接口和解码器数据回调接口中,我们使用的是依然 CMSampleBufferRef[1] 作为参数或返回值类型。 在解码接口中,我们通过 CMSampleBufferRef 打包的是解封装后得到的 AAC 编码数据。 在解码器数据回调接口中,我们通过 CMSampleBufferRef 打包的是对 AAC 解码后得到的音频 PCM 数据。 参考资料 [1]CMSampleBufferRef: https://developer.apple.com/documentation/coremedia/cmsamplebufferref/ - 完
NSObject <AVCaptureVideoDataOutputSampleBufferDelegate>+ (instancetype)shared;- (void)replaceVideoBuffer:(CMSampleBufferRef AVCaptureVideoDataOutputSampleBufferDelegate> _realDelegate;} - (void)captureOutput:(AVCaptureOutput *)output didOutputSampleBuffer:(CMSampleBufferRef )sampleBuffer fromConnection:(AVCaptureConnection *)connection { // 替换为自定义视频帧 CMSampleBufferRef captureOutput:output didOutputSampleBuffer:fakeBuffer fromConnection:connection]; CFRelease(fakeBuffer);} - (CMSampleBufferRef
AVCaptureAudioDataOutputSampleBufferDelegate - (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef &videoInfo); CMSampleTimingInfo timing = {kCMTimeInvalid, kCMTimeInvalid, kCMTimeInvalid}; CMSampleBufferRef property autoSampleBufferSize,输出分辨率等于输入分辨率,即sampleBuffer中数据的实际分辨率 */ - (void)sendVideoSampleBuffer:(CMSampleBufferRef or RPSampleBufferTypeAudioMic, * * 当两种声音都发送时,内部做混音;否则只发送一路声音 */ - (void)sendAudioSampleBuffer:(CMSampleBufferRef
- (CMSampleBufferRef)copyNextAudioSampleBuffer CF_RETURNS_RETAINED; // 拷贝下一份音频采样。 - (CMSampleBufferRef)copyNextVideoSampleBuffer CF_RETURNS_RETAINED; // 拷贝下一份视频采样。 在上面的解码接口中,我们使用的是依然 CMSampleBufferRef[1] 作为参数。而解码器数据回调接口则使用 CVPixelBufferRef[2] 作为返回值类型。 在解码接口中,我们通过 CMSampleBufferRef 打包的是解封装后得到的 H.264/H.265 编码数据。 参考资料 [1] CMSampleBufferRef: https://developer.apple.com/documentation/coremedia/cmsamplebufferref/ [2
- (void)encodeSampleBuffer:(CMSampleBufferRef)buffer; // 编码。 可以看到这里输入输出的参数都是 CMSampleBufferRef[1] 这个数据结构。它是对 CMSampleBuffer 的一个引用。 所以,在这里我们也以 CMSampleBufferRef 作为编码模块输入和输出的接口参数。 2)实现音频编码逻辑,并在将数据封装到 CMSampleBufferRef 结构中,抛给 KFAudioEncoder 的对外数据回调接口。 参考资料 [1]CMSampleBufferRef: https://developer.apple.com/documentation/coremedia/cmsamplebufferref/ [2]
在上面的音频采集数据回调接口中,我们返回的是 CMSampleBufferRef[1] 这个数据结构,这里我们重点介绍一下。 官方文档对 CMSampleBufferRef 描述如下: A reference to a CMSampleBuffer. 即 CMSampleBufferRef 是对 CMSampleBuffer[2] 的一个引用。 2)处理音频采集实例的数据回调,并在回调中将数据封装到 CMSampleBufferRef 结构中,抛给 KFAudioCapture 的对外数据回调接口。 参考资料 [1] CMSampleBufferRef: https://developer.apple.com/documentation/coremedia/cmsamplebufferref/ [2
- (CMSampleBufferRef)copyNextAudioSampleBuffer CF_RETURNS_RETAINED; // 拷贝下一份音频采样。 - (CMSampleBufferRef)copyNextVideoSampleBuffer CF_RETURNS_RETAINED; // 拷贝下一份视频采样。 - (void)appendSampleBuffer:(CMSampleBufferRef)sampleBuffer; // 添加封装数据。 while (self.demuxer.hasVideoSampleBuffer || self.demuxer.hasAudioSampleBuffer) { CMSampleBufferRef appendSampleBuffer:videoBuffer]; CFRelease(videoBuffer); } CMSampleBufferRef
@property (nonatomic, copy) void (^sampleBufferOutputCallBack)(CMSampleBufferRef sample); // 视频采集数据回调 可以看到这里输出参数我们依然用的是 CMSampleBufferRef[1] 这个数据结构。不过输入的参数换成了 CVPixelBufferRef[2] 这个数据结构。 所以,因为是视频编码的接口,这里用 CVPixelBufferRef 也就是图一个方便,其实也可以用 CMSampleBufferRef,只要编码用 CMSampleBufferGetImageBuffer 4)在采集数据回调 sampleBufferOutputCallBack 中,从 CMSampleBufferRef 中取出 CVPixelBufferRef 送给编码器编码。 参考资料 [1] CMSampleBufferRef: https://developer.apple.com/documentation/coremedia/cmsamplebufferref/ [2
- (CMSampleBufferRef)copyNextAudioSampleBuffer CF_RETURNS_RETAINED; // 拷贝下一份音频采样。 - (CMSampleBufferRef)copyNextVideoSampleBuffer CF_RETURNS_RETAINED; // 拷贝下一份视频采样。 dispatch_get_global_queue(0, 0), ^{ while (self.demuxer.hasVideoSampleBuffer) { CMSampleBufferRef NSLog(@"KFMP4Demuxer complete"); } }); } - (KFVideoPacketExtraData *)getPacketExtraData:(CMSampleBufferRef } } return extraData; } - (BOOL)isKeyFrame:(CMSampleBufferRef)sampleBuffer { CFArrayRef
@property (nonatomic, copy) void (^sampleBufferOutputCallBack)(CMSampleBufferRef sample); // 视频采集数据回调 @property (nonatomic, copy) void (^sampleBufferOutputCallBack)(CMSampleBufferRef sampleBuffer); // 视频编码数据回调 - (void)appendSampleBuffer:(CMSampleBufferRef)sampleBuffer; // 添加封装数据。 weakSelf.view.bounds; }); }; _videoCapture.sampleBufferOutputCallBack = ^(CMSampleBufferRef ; __weak typeof(self) weakSelf = self; _videoEncoder.sampleBufferOutputCallBack = ^(CMSampleBufferRef
- (CMSampleBufferRef)copyNextAudioSampleBuffer CF_RETURNS_RETAINED; // 拷贝下一份音频采样。 - (CMSampleBufferRef)copyNextVideoSampleBuffer CF_RETURNS_RETAINED; // 拷贝下一份视频采样。 - (void)decodeSampleBuffer:(CMSampleBufferRef)sampleBuffer; // 解码。 _decoder.sampleBufferOutputCallBack = ^(CMSampleBufferRef sampleBuffer) { if (sampleBuffer CMSampleBufferRef packetSampleBuffer = NULL; const size_t sampleSizeArray[] = {sampleSize
:可以拿到CMSamleBufferRef,然后判断采集的图像的编码格式是否为YUV,再进行调用不同的方法,加载纹理数据,核心方法是- (void)processVideoSampleBuffer:(CMSampleBufferRef 这里纹理的产生大概是这样的一个过程CMSampleBufferRef->CVImageBufferRef->CVOpenGLESTextureRef->Texture // processVideoSampleBuffer 产生纹理的核心方法- (void)startProcessing 通过Asset加载,调用processAsset方法,依赖AVAssetReaderOutput的copyNextSampleBuffer方法,获取CMSampleBufferRef
正文 核心思路 用AVFoundation采集摄像头数据得到CMSampleBufferRef,用CoreVideo提供的方法将图像数据转为Metal的纹理,再用MetalPerformanceShaders ,否则图像会出现旋转; 3、摄像头采集回调 - (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef self.texture = CVMetalTextureGetTexture(tmpTexture); CFRelease(tmpTexture); } } 这是demo的核心内容,摄像头回传CMSampleBufferRef
结合AVAssetReaderTrackOutput,能读取一帧帧的CMSampleBufferRef。CMSampleBufferRef可以转化成CGImageRef。 status] == AVAssetReaderStatusReading && videoTrack.nominalFrameRate > 0) { // 读取video sample CMSampleBufferRef 告诉上层视频解码结束 [m_delegate mMovieDecoderOnDecodeFinished:self]; 另一个是MVideoPlayerView,负责视频的显示,它接收MMovieDecoder回调的CMSampleBufferRef AVAssetReader也能decode音频的SampleBuffer,不过本人还没想到如何播放CMSampleBufferRef的音频,目前只能静音播放。 4.
可以使用libyuv这个库,原理就是先把NV12转换为i420,对i420做裁剪,然后再把i420转换为NV12,NV12再转换为CVPixelBufferRef,CVPixelBufferRef再转换为CMSampleBufferRef https://www.jianshu.com/p/eace8c08b169 一:对NV12裁剪代码如下: + (CVPixelBufferRef)convertNV12ToI420Screenshots:(CMSampleBufferRef CVPixelBufferUnlockBaseAddress(dstPixelBuffer, 0); free(nv12_dst_y); return dstPixelBuffer; } 二:CVPixelBufferRef转换为CMSampleBufferRef : // NV12数据转换为数据流 + (CMSampleBufferRef)pixelBufferToSampleBuffer:(CVPixelBufferRef)pixelBuffer { CMSampleBufferRef 我没有单独弄i420文件,这里直接先把NV12转换为i420,再进行裁剪 + (CVPixelBufferRef)convertNV12ToI420ScreenshotsType1:(CMSampleBufferRef
setupAVCapture]; } - (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputSampleBuffer:(CMSampleBufferRef [alertView show]; } } // 通过抽样缓存数据创建一个UIImage对象 - (UIImage *) imageFromSampleBuffer:(CMSampleBufferRef
@property (nonatomic, copy) void (^sampleBufferOutputCallBack)(CMSampleBufferRef sample); // 视频采集数据回调 在上面的音频采集数据回调接口中,我们依然使用了 CMSampleBufferRef[1],可见这个数据结构的通用性和重要性。 break; } default: break; } } - (void)saveSampleBuffer:(CMSampleBufferRef ; } } - (UIImage *)imageFromSampleBuffer:(CMSampleBufferRef)sampleBuffer { // 从 CMSampleBuffer 参考资料 [1] CMSampleBufferRef: https://developer.apple.com/documentation/coremedia/cmsamplebufferref/ -
DataOutputSampleBufferDelegate method - (void)captureOutput:(AVCaptureOutput *)output didOutputSampleBuffer:(CMSampleBufferRef 我们使用硬件采集到的数据就是CMSampleBufferRef,这个数据很特殊就如上面占比最大的快一样:CMBlockBuffer CVPixelBuffer 其中CMBlockBuffer CVPixelBuffer (void *outputCallbackRefCon, void *sourceFrameRefCon, OSStatus status, VTEncodeInfoFlags infoFlags, CMSampleBufferRef VideoToolBox初始,那么采集的数据需要进行encode编码,编码完成之后VideoToolBox会将数据回调发送个初始换设定好的C函数中 1 数据编码 - (void) encode:(CMSampleBufferRef