美文网首页音视频
关于iOS下音频操作(音频合并,裁切,转码)

关于iOS下音频操作(音频合并,裁切,转码)

作者: 牧羊人Q | 来源:发表于2017-08-17 20:56 被阅读265次

前段时间在做音频的相关技术,涉及到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);

}

}];

相关文章

网友评论

  • astring:楼主借工具类来研究一下🙏
  • 奇少灬:楼主, 有demo么, 发我一份供参考研究. 多谢..QQ732556518
    牧羊人Q:@奇少灬 是要拼接的
    奇少灬:@牧羊人Q 多谢楼主, 代码收到了, 有个问题请教一下. 就是如果剪切完之后在这个.wav的基础上继续录音要怎么做呢. :sweat: 是不是要做一个拼接了 ? 还是还能继续录制 ?
    牧羊人Q:我给我项目里的工具类发给你了 你参考下

本文标题:关于iOS下音频操作(音频合并,裁切,转码)

本文链接:https://www.haomeiwen.com/subject/vqinrxtx.html