免费网站av源码分享大全 免费资 源网 站

大家好,今天来为大家分享免费网站av源码分享大全的一些知识点,和站的问题解析,大家要是都明白,那么可以忽略,如果不太清楚的话可以看看本篇文章,相信很大概率可以解决您的问题,接下来我们就一起来看看吧!

iOS/Android客户端开发同学如果想要开始学习音视频开发,最丝滑的方式是对音视频基础概念知识有一定了解后,再借助iOS/Android平台的音视频能力上手去实践音视频的采集→编码→封装→解封装→解码→渲染过程,并借助音视频工具来分析和理解对应的音视频数据。

在音视频工程示例这个栏目,我们将通过拆解采集→编码→封装→解封装→解码→渲染流程并实现Demo来向大家介绍如何在iOS/Android平台上手音视频开发。

这里是第六篇:iOS音频渲染Demo。这个Demo里包含以下内容:

1)实现一个音频解封装模块;2)实现一个音频解码模块;3)实现一个音频渲染模块;4)实现对MP4文件中音频部分的解封装和解码逻辑,并将解封装、解码后的数据送给渲染模块播放;5)详尽的代码注释,帮你理解代码逻辑和原理。

1、音频解封装模块

在这个Demo中,解封装模块KFMP4Demuxer的实现与《iOS音频解封装Demo》中一样,这里就不再重复介绍了,其接口如下:

KFMP4Demuxer.h\nimport<CoreMedia/CoreMedia.h>\n34;KFDemuxerConfig.h&import<Foundation/Foundation.h>\nimport<Foundation/Foundation.h>\nimport&34;\n\npragmamark-Lifecycle\n-(instancetype)initWithChannels:(NSInteger)channelsbitDepth:(NSInteger)bitDepthsampleRate:(NSInteger)sampleRate{\nself=[superinit];\nif(self){\n_audioChannels=channels;\n_bitDepth=bitDepth;\n_audioSampleRate=sampleRate;\n_renderQueue=dispatch_queue_create(&34;,DISPATCH_QUEUE_SERIAL);\n}\n\nreturnself;\n}\n\n-(void)dealloc{\n//清理音频渲染实例。\nif(_audioRenderInstance){\nAudioOutputUnitStop(_audioRenderInstance);\nAudioUnitUninitialize(_audioRenderInstance);\nAudioComponentInstanceDispose(_audioRenderInstance);\n_audioRenderInstance=nil;\n}\n}\n\npragmamark-PrivateMethod\n-(void)_setupAudioRenderInstance:(NSError**)error{\n//1、设置音频组件描述。\nAudioComponentDescriptionaudioComponentDescription={\n.componentType=kAudioUnitType_Output,\n//.componentSubType=kAudioUnitSubType_VoiceProcessingIO,//回声消除模式\n.componentSubType=kAudioUnitSubType_RemoteIO,\n.componentManufacturer=kAudioUnitManufacturer_Apple,\n.componentFlags=0,\n.componentFlagsMask=0\n};\n\n//2、查找符合指定描述的音频组件。\nAudioComponentinputComponent=AudioComponentFindNext(NULL,&audioComponentDescription);\n\n//3、创建音频组件实例。\nOSStatusstatus=AudioComponentInstanceNew(inputComponent,&_audioRenderInstance);\nif(status!=noErr){\n*error=[NSErrorerrorWithDomain:NSStringFromClass(self.class)code:statususerInfo:nil];\nreturn;\n}\n\n//4、设置实例的属性:可读写。0不可读写,1可读写。\nUInt32flag=1;\nstatus=AudioUnitSetProperty(_audioRenderInstance,kAudioOutputUnitProperty_EnableIO,kAudioUnitScope_Output,OutputBus,&flag,sizeof(flag));\nif(status!=noErr){\n*error=[NSErrorerrorWithDomain:NSStringFromClass(self.class)code:statususerInfo:nil];\nreturn;\n}\n\n//5、设置实例的属性:音频参数,如:数据格式、声道数、采样位深、采样率等。\nAudioStreamBasicDescriptioninputFormat={0};\ninputFormat.mFormatID=kAudioFormatLinearPCM;//原始数据为PCM,采用声道交错格式。\ninputFormat.mFormatFlags=kAudioFormatFlagIsSignedInteger|kAudioFormatFlagsNativeEndian|kAudioFormatFlagIsPacked;\ninputFormat.mChannelsPerFrame=(UInt32)self.audioChannels;//每帧的声道数。\ninputFormat.mFramesPerPacket=1;//每个数据包帧数。\ninputFormat.mBitsPerChannel=(UInt32)self.bitDepth;//采样位深。\ninputFormat.mBytesPerFrame=inputFormat.mChannelsPerFrame*inputFormat.mBitsPerChannel/8;//每帧字节数(byte=bit/8)。\ninputFormat.mBytesPerPacket=inputFormat.mFramesPerPacket*inputFormat.mBytesPerFrame;//每个包字节数。\ninputFormat.mSampleRate=self.audioSampleRate;//采样率\nstatus=AudioUnitSetProperty(_audioRenderInstance,kAudioUnitProperty_StreamFormat,kAudioUnitScope_Input,OutputBus,&inputFormat,sizeof(inputFormat));\nif(status!=noErr){\n*error=[NSErrorerrorWithDomain:NSStringFromClass(self.class)code:statususerInfo:nil];\nreturn;\n}\n\n//6、设置实例的属性:数据回调函数。\nAURenderCallbackStructrenderCallbackRef={\n.inputProc=audioRenderCallback,\n.inputProcRefCon=(__bridgevoid*)(self)//对应回调函数中的*inRefCon。\n};\nstatus=AudioUnitSetProperty(_audioRenderInstance,kAudioUnitProperty_SetRenderCallback,kAudioUnitScope_Global,OutputBus,&renderCallbackRef,sizeof(renderCallbackRef));\nif(status!=noErr){\n*error=[NSErrorerrorWithDomain:NSStringFromClass(self.class)code:statususerInfo:nil];\nreturn;\n}\n\n//7、初始化实例。\nstatus=AudioUnitInitialize(_audioRenderInstance);\nif(status!=noErr){\n*error=[NSErrorerrorWithDomain:NSStringFromClass(self.class)code:statususerInfo:nil];\nreturn;\n}\n}\n\n-(void)_callBackError:(NSError*)error{\nself.isError=YES;\nif(self.errorCallBack){\ndispatch_async(dispatch_get_main_queue(),^{\nself.errorCallBack(error);\n});\n}\n}\n\nimport&34;\nimport&34;\n34;KFMP4Demuxer.h&import&34;\n34;KFWeakProxy.h&defineKFDecoderMaxCache4096*5//解码数据缓冲区最大长度。\n\n@interfaceKFAudioRenderViewController()\n@property(nonatomic,strong)KFDemuxerConfig*demuxerConfig;\n@property(nonatomic,strong)KFMP4Demuxer*demuxer;\n@property(nonatomic,strong)KFAudioDecoder*decoder;\n@property(nonatomic,strong)KFAudioRender*audioRender;\n@property(nonatomic,strong)dispatch_semaphore_tsemaphore;\n@property(nonatomic,strong)NSMutableData*pcmDataCache;//解码数据缓冲区。\n@property(nonatomic,assign)NSIntegerpcmDataCacheLength;\n@property(nonatomic,strong)CADisplayLink*timer;\n@end\n\n@implementationKFAudioRenderViewController\n34;input&34;mp4&34;KFMP4Demuxererror:%zi%@&34;KFAudioDecodererror:%zi%@&34;KFAudioRendererror:%zi%@&pragmamark-Lifecycle\n-(void)viewDidLoad{\n[superviewDidLoad];\n\n_semaphore=dispatch_semaphore_create(1);\n_pcmDataCache=[[NSMutableDataalloc]init];\n\n[selfsetupAudioSession];\n[selfsetupUI];\n\n//通过一个timer来保证持续从文件中解封装和解码一定量的数据。\n_timer=[CADisplayLinkdisplayLinkWithTarget:[KFWeakProxyproxyWithTarget:self]selector:@selector(timerCallBack:)];\n[_timeraddToRunLoop:[NSRunLoopmainRunLoop]forMode:NSRunLoopCommonModes];\n[_timersetPaused:NO];\n\n[self.demuxerstartReading:^(BOOLsuccess,NSError*_Nonnullerror){\nNSLog(@&34;,success);\n}];\n}\n\n-(void)dealloc{\n\n}\n\n34;AudioRender&34;Start&34;Stop&pragmamark-Action\n-(void)startRender{\n[self.audioRenderstartPlaying];\n}\n\n-(void)stopRender{\n[self.audioRenderstopPlaying];\n}\n\n34;AVAudioSessionsetCategoryerror&34;AVAudioSessionsetActiveerror&34;SampleNum:%ld”,CMSampleBufferGetNumSamples(sampleBuffer));\nfor(NSIntegerindex=0;index<CMSampleBufferGetNumSamples(sampleBuffer);index++){\n//1、获取一个包的数据。\nsize_tsampleSize=CMSampleBufferGetSampleSize(sampleBuffer,index);\nCMSampleTimingInfotimingInfo;\nCMSampleBufferGetSampleTimingInfo(sampleBuffer,index,&timingInfo);\nchar*sampleDataPointer=malloc(sampleSize);\nmemcpy(sampleDataPointer,dataPointer,sampleSize);\n\n//2、将数据封装到CMBlockBuffer中。\nCMBlockBufferRefpacketBlockBuffer;\nOSStatusstatus=CMBlockBufferCreateWithMemoryBlock(kCFAllocatorDefault,\nsampleDataPointer,\nsampleSize,\nNULL,\nNULL,\n0,\nsampleSize,\n0,\n&packetBlockBuffer);\n\nif(status==noErr){\n//3、将CMBlockBuffer封装到CMSampleBuffer中。\nCMSampleBufferRefpacketSampleBuffer=NULL;\nconstsize_tsampleSizeArray[]={sampleSize};\nstatus=CMSampleBufferCreateReady(kCFAllocatorDefault,\npacketBlockBuffer,\nCMSampleBufferGetFormatDescription(sampleBuffer),\n1,\n1,\n&timingInfo,\n1,\nsampleSizeArray,\n&packetSampleBuffer);\nCFRelease(packetBlockBuffer);\n\n//4、解码这个包的数据。\nif(packetSampleBuffer){\n[self.decoderdecodeSampleBuffer:packetSampleBuffer];\nCFRelease(packetSampleBuffer);\n}\n}\ndataPointer+=sampleSize;\n}\n}\n\n@end

上面是KFAudioRenderViewController的实现,其中主要包含这几个部分:

1)在页面加载完成后就启动解封装和解码模块,并通过一个timer来驱动解封装器和解码器。在-viewDidLoad中实现。2)定时从文件中解封装一定量(不超过KFDecoderMaxCache)的数据送给解码器。在-timerCallBack:方法中实现。3)解封装后,需要将数据拆包,以包为单位封装为CMSampleBuffer送给解码器解码。在-decodeSampleBuffer:方法中实现。4)在解码模块KFAudioDecoder的数据回调中获取解码后的PCM数据缓冲起来等待渲染。在KFAudioDecoder的sampleBufferOutputCallBack回调中实现。5)在渲染模块KFAudioRender的输入数据回调中把缓冲区的数据交给系统音频渲染单元渲染。在KFAudioRender的audioBufferInputCallBack回调中实现。

更具体细节见上述代码及其注释。

原文链接:iOSAVDemo(6):音频渲染,免费获得源码丨音视频工程示例

免费网站av源码分享大全和站的问题分享结束啦,以上的文章解决了您的问题吗?欢迎您下次再来哦!

Published by

风君子

独自遨游何稽首 揭天掀地慰生平