前段时间在做音频的相关技术,涉及到iOS录音转码的一些知识,这里在这里记录下。
一、使用iOS自带的AVAudioRecorder录音时录音的格式可以是.caf,wav,但有时候可能需要需要把这些格式转成其他的格式,比如从caf转为mp3,从wav转mp3,m4a转wav再转mp3(因为有时候在对音频做合并和剪切的时候生成的音频格式是m4a的)这里就说一说音频格式的集中转码操作。
1、caf音频格式转MP3
caf转mp3音频格式需要用到一个c语言的第三方库lame,这个库可以把caf和wav的成功转为mp3格式(其他格式的没有测试)。只需设置一些参数就可以了,详见代码:
@try{
intread, write;
FILE*pcm =fopen([originalPathcStringUsingEncoding:1],"rb");//被转换的文件
fseek(pcm,4*1024,SEEK_CUR);//skip file header
FILE*mp3 =fopen([outPathcStringUsingEncoding:1],"wb");//转换后文件的存放位置
constintPCM_SIZE =8192;
constintMP3_SIZE =8192;
shortintpcm_buffer[PCM_SIZE*2];
unsignedcharmp3_buffer[MP3_SIZE];
lame_tlame =lame_init();
lame_set_num_channels(lame,2);//设置1为单通道,默认为2双通道
lame_set_in_samplerate(lame,44100);采样率,这里一般和录音时设置的值一致
lame_set_brate(lame,8);
lame_set_mode(lame,3);
lame_set_VBR(lame,vbr_default);
lame_set_quality(lame,2);/* 2=high5 = medium7=low音质*/
lame_init_params(lame);
do{
read =fread(pcm_buffer,2*sizeof(shortint), PCM_SIZE, pcm);
if(read ==0)
write =lame_encode_flush(lame, mp3_buffer, MP3_SIZE);
else
write =lame_encode_buffer_interleaved(lame, pcm_buffer, read, mp3_buffer, MP3_SIZE);
fwrite(mp3_buffer, write,1, mp3);
}while(read !=0);
lame_close(lame);
fclose(mp3);
fclose(pcm);
}
@catch(NSException *exception) {
// NSLog(@"%@",[exception description]);
}
@finally{
successBlock(outPath);//返回路径
}
2、m4a格式转wav格式
因为在对音频做合并或者裁切的时候生成的音频格式是m4a的,但是m4a转成mp3会损坏音频格式,所以我当时采用先把m4a转为wav,再用wav转成mp3。以下粘出代码:
NSURL*originalUrl = [NSURLfileURLWithPath:originalPath];
NSURL*outPutUrl = [NSURLfileURLWithPath:outPath];
AVURLAsset*songAsset = [AVURLAssetURLAssetWithURL:originalUrloptions:nil];//读取原始文件信息
NSError*error =nil;
AVAssetReader*assetReader = [AVAssetReaderassetReaderWithAsset:songAsseterror:&error];
if(error) {
NSLog(@"error: %@", error);
return;
}
AVAssetReaderOutput*assetReaderOutput = [AVAssetReaderAudioMixOutputassetReaderAudioMixOutputWithAudioTracks:songAsset.tracksaudioSettings:nil];
if(![assetReadercanAddOutput:assetReaderOutput]) {
NSLog(@"can't add reader output... die!");
return;
}
[assetReaderaddOutput:assetReaderOutput];
AVAssetWriter*assetWriter = [AVAssetWriterassetWriterWithURL:outPutUrlfileType:AVFileTypeCoreAudioFormaterror:&error];
if(error) {
NSLog(@"error: %@", error);
return;
}
AudioChannelLayoutchannelLayout;
memset(&channelLayout,0,sizeof(AudioChannelLayout));
channelLayout.mChannelLayoutTag=kAudioChannelLayoutTag_Stereo;
NSDictionary*outputSettings = [NSDictionarydictionaryWithObjectsAndKeys:[NSNumbernumberWithInt:kAudioFormatLinearPCM],AVFormatIDKey,[NSNumbernumberWithFloat:44100.0],AVSampleRateKey,[NSNumbernumberWithInt:2],AVNumberOfChannelsKey,[NSDatadataWithBytes:&channelLayoutlength:sizeof(AudioChannelLayout)],AVChannelLayoutKey,[NSNumbernumberWithInt:16],AVLinearPCMBitDepthKey,[NSNumbernumberWithBool:NO],AVLinearPCMIsNonInterleaved,[NSNumbernumberWithBool:NO],AVLinearPCMIsFloatKey,[NSNumbernumberWithBool:NO],AVLinearPCMIsBigEndianKey,nil];
AVAssetWriterInput*assetWriterInput = [AVAssetWriterInputassetWriterInputWithMediaType:AVMediaTypeAudiooutputSettings:outputSettings];
if([assetWritercanAddInput:assetWriterInput]) {
[assetWriteraddInput:assetWriterInput];
}else{
NSLog(@"can't add asset writer input... die!");
return;
}
assetWriterInput.expectsMediaDataInRealTime=NO;
[assetWriterstartWriting];
[assetReaderstartReading];
AVAssetTrack*soundTrack = [songAsset.tracksobjectAtIndex:0];
CMTimestartTime =CMTimeMake(0, soundTrack.naturalTimeScale);
[assetWriterstartSessionAtSourceTime:startTime];
__blockUInt64convertedByteCount =0;
dispatch_queue_tmediaInputQueue =dispatch_queue_create("mediaInputQueue",NULL);
[assetWriterInputrequestMediaDataWhenReadyOnQueue:mediaInputQueueusingBlock: ^{
while(assetWriterInput.readyForMoreMediaData) {
CMSampleBufferRefnextBuffer = [assetReaderOutputcopyNextSampleBuffer];
if(nextBuffer) {
// append buffer
[assetWriterInputappendSampleBuffer: nextBuffer];
convertedByteCount +=CMSampleBufferGetTotalSampleSize(nextBuffer);
}else{
[assetWriterInputmarkAsFinished];
[assetWriterfinishWritingWithCompletionHandler:^{
}];
[assetReadercancelReading];
NSDictionary*outputFileAttributes = [[NSFileManagerdefaultManager]attributesOfItemAtPath:[outPutUrlpath]error:nil];
NSLog(@"FlyElephant %lld",[outputFileAttributesfileSize]);
if([FileUitlisExist:originalPath]) {
[FileUitlremoveFile:originalPath];
}
block(outPath);
break;
}
}
}];
简单的来说用AVAssetReader和AVAssetWrite,AVAssetReader用于从AVAsset资源读取媒体样本,AVAssetWrite用于对媒体资源进行编码并写入到新的文件中。然后wav转mp3就按照第一步的那样做就可以了。
3、音频合并
音频合并和裁切用的AVFoundation框架下一个多媒体的载体类:AVAsset。它提供了一系列的接口来处理多媒体,只需要我们写很少的代码就能对音频,视频做出来。下面贴出代码:
AVURLAsset*audioAsset1 = [AVURLAssetassetWithURL:[NSURLfileURLWithPath:audio2]];
AVURLAsset*audioAsset2 = [AVURLAssetassetWithURL:[NSURLfileURLWithPath:audio1]];
AVMutableComposition*composition = [AVMutableCompositioncomposition];
//音频通道
AVMutableCompositionTrack*audioTrack1 = [compositionaddMutableTrackWithMediaType:AVMediaTypeAudiopreferredTrackID:0];
AVMutableCompositionTrack*audioTrack2 = [compositionaddMutableTrackWithMediaType:AVMediaTypeAudiopreferredTrackID:0];
//音频采集通道
AVAssetTrack*audioAssetTrack1 = [[audioAsset1tracksWithMediaType:AVMediaTypeAudio]firstObject];
AVAssetTrack*audioAssetTrack2 = [[audioAsset2tracksWithMediaType:AVMediaTypeAudio]firstObject];
//音频合并-插入音轨文件
[audioTrack1insertTimeRange:CMTimeRangeMake(kCMTimeZero, audioAsset1.duration)ofTrack:audioAssetTrack1atTime:kCMTimeZeroerror:nil];
// `startTime`参数要设置为第一段音频的时长,即`audioAsset1.duration`,表示将第二段音频插入到第一段音频的尾部。
[audioTrack2insertTimeRange:CMTimeRangeMake(kCMTimeZero, audioAsset2.duration)ofTrack:audioAssetTrack2atTime:audioAsset1.durationerror:nil];
//合并后的文件导出- `presetName`要和之后的`session.outputFileType`相对应。
AVAssetExportSession*session = [[AVAssetExportSessionalloc]initWithAsset:compositionpresetName:AVAssetExportPresetAppleM4A];
NSString*outPutFilePath = [selfm4aRecordPath];
if([[NSFileManagerdefaultManager]fileExistsAtPath:outPutFilePath]) {
[[NSFileManagerdefaultManager]removeItemAtPath:outPutFilePatherror:nil];
}
//查看当前session支持的fileType类型
NSLog(@"---%@",[sessionsupportedFileTypes]);
session.outputURL= [NSURLfileURLWithPath:outPutFilePath];
session.outputFileType=AVFileTypeAppleM4A;//与上述的`present`相对应
session.shouldOptimizeForNetworkUse=YES;//优化网络
WeakSelf(self);
[sessionexportAsynchronouslyWithCompletionHandler:^{
if(session.status==AVAssetExportSessionStatusCompleted) {
NSLog(@"合并成功----%@", outPutFilePath);
}elseif(session.status==AVAssetExportSessionStatusFailed){
NSLog(@"合并失败!");
}
}];
4、音频裁剪
//音频输出会话
AVURLAsset*videoAsset = [AVURLAssetassetWithURL:[NSURLfileURLWithPath:path]];
//音频输出会话
//AVAssetExportPresetAppleM4A:(输出音频,并且是.m4a格式)
AVAssetExportSession*exportSession = [AVAssetExportSessionexportSessionWithAsset:videoAssetpresetName:AVAssetExportPresetAppleM4A];
exportSession.outputURL= [NSURLfileURLWithPath:m4aOutPath];
exportSession.outputFileType=AVFileTypeAppleM4A;
exportSession.timeRange=CMTimeRangeFromTimeToTime(CMTimeMake(source,1),CMTimeMake(end,1));
[exportSessionexportAsynchronouslyWithCompletionHandler:^{
//exporeSession.status
if(AVAssetExportSessionStatusCompleted== exportSession.status) {
[selfconvertM4aToWav:m4aOutPathoutPath:wavOutPathsuccess:^(idparameter) {
block(parameter);
}];
}elseif(AVAssetExportSessionStatusFailed== exportSession.status) {
NSLog(@"剪切失败!");
}else{
NSLog(@"Export Session Status: %ld", (long)exportSession.status);
}
}];
网友评论