美文网首页音视频
关于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