学习笔记 Learning AVFoundation

1,119 阅读3分钟

book.jpg

前言

AVFoundation框架的功能十分强大,同时也充斥着各种类,比如各种session,各种input,各种output。之前并没有对整个框架进行过完整梳理,虽然也能东拼西凑的那各种类完成需求,但遇到深度定制的需求以及踩到坑之后,总会比较吃力。最近,在看《learning AV Foundation》这本书,看完后尝试着梳理了一个整体脉络,

//并将理解的知识点整合用Swift重写了一个demo,包括拍摄+实时滤镜+实时写入+自定义导出分辨率等。

1. 文字转语音AVSpeechSynthesizer
        _synthesizer = [[AVSpeechSynthesizer alloc] init];

        _voices = @[[AVSpeechSynthesisVoice voiceWithLanguage:@"en-US"],
//                    [AVSpeechSynthesisVoice voiceWithLanguage:@"en-GB"]
                    [AVSpeechSynthesisVoice voiceWithLanguage:@"zh-HK"]
                    ];
2.音频混响
- (AVAudioPlayer *)playerForFile:(NSString *)name {

    NSURL *fileURL = [[NSBundle mainBundle] URLForResource:name
                                             withExtension:@"caf"];

    NSError *error;
    AVAudioPlayer *player = [[AVAudioPlayer alloc] initWithContentsOfURL:fileURL
                                                                   error:&error];
    if (player) {
        player.numberOfLoops = -1; // loop indefinitely
        player.enableRate = YES;
        [player prepareToPlay];
    } else {
        NSLog(@"Error creating player: %@", [error localizedDescription]);
    }

    return player;
}
        {
        AVAudioPlayer *guitarPlayer = [self playerForFile:@"guitar"];
        AVAudioPlayer *bassPlayer = [self playerForFile:@"bass"];
        AVAudioPlayer *drumsPlayer = [self playerForFile:@"drums"];

        guitarPlayer.delegate = self;

        _players = @[guitarPlayer, bassPlayer, drumsPlayer];

}
- (void)play {
    if (!self.playing) {
        NSTimeInterval delayTime = [self.players[0] deviceCurrentTime] + 0.01;
        for (AVAudioPlayer *player in self.players) {
            [player playAtTime:delayTime];
        }
        self.playing = YES;
    }
}
3. 音频录制AVAudioRecorder
    if (self) {
        NSString *tmpDir = NSTemporaryDirectory();
        NSString *filePath = [tmpDir stringByAppendingPathComponent:@"memo.caf"];
        NSURL *fileURL = [NSURL fileURLWithPath:filePath];

        NSDictionary *settings = @{
                                   AVFormatIDKey : @(kAudioFormatAppleIMA4),
                                   AVSampleRateKey : @44100.0f,
                                   AVNumberOfChannelsKey : @1,
                                   AVEncoderBitDepthHintKey : @16,
                                   AVEncoderAudioQualityKey : @(AVAudioQualityMedium)
                                   };

        NSError *error;
        self.recorder = [[AVAudioRecorder alloc] initWithURL:fileURL settings:settings error:&error];
        if (self.recorder) {
            self.recorder.delegate = self;
            self.recorder.meteringEnabled = YES;
            [self.recorder prepareToRecord];
        } else {
            NSLog(@"Error: %@", [error localizedDescription]);
        }

    }

4. 视频播放
- (id)initWithURL:(NSURL *)assetURL {
    self = [super init];
    if (self) {
        _asset = [AVAsset assetWithURL:assetURL];                           // 1
        [self prepareToPlay];
    }
    return self;
}

- (void)prepareToPlay {
    NSArray *keys = @[
        @"tracks",
        @"duration",
        @"commonMetadata",
        @"availableMediaCharacteristicsWithMediaSelectionOptions"
    ];
    self.playerItem = [AVPlayerItem playerItemWithAsset:self.asset          // 2
                           automaticallyLoadedAssetKeys:keys];

    [self.playerItem addObserver:self                                       // 3
                      forKeyPath:STATUS_KEYPATH
                         options:0
                         context:&PlayerItemStatusContext];

    self.player = [AVPlayer playerWithPlayerItem:self.playerItem];          // 4

    self.playerView = [[THPlayerView alloc] initWithPlayer:self.player];    // 5
}
5. 视频录制/拍照
- (BOOL)setupSession:(NSError **)error {

	self.captureSession = [[AVCaptureSession alloc] init];                  // 1
    self.captureSession.sessionPreset = AVCaptureSessionPresetHigh;

	// Set up default camera device
	AVCaptureDevice *videoDevice =                                          // 2
        [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeVideo];

    AVCaptureDeviceInput *videoInput =                                      // 3
        [AVCaptureDeviceInput deviceInputWithDevice:videoDevice error:error];
    if (videoInput) {
        if ([self.captureSession canAddInput:videoInput]) {                 // 4
            [self.captureSession addInput:videoInput];
            self.activeVideoInput = videoInput;
        }
    } else {
        return NO;
    }

    // Setup default microphone
    AVCaptureDevice *audioDevice =                                          // 5
        [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeAudio];

    AVCaptureDeviceInput *audioInput =                                      // 6
        [AVCaptureDeviceInput deviceInputWithDevice:audioDevice error:error];
    if (audioInput) {
        if ([self.captureSession canAddInput:audioInput]) {                 // 7
            [self.captureSession addInput:audioInput];
        }
    } else {
        return NO;
    }

	// Setup the still image output
    self.imageOutput = [[AVCaptureStillImageOutput alloc] init];            // 8
    self.imageOutput.outputSettings = @{AVVideoCodecKey : AVVideoCodecJPEG};

	if ([self.captureSession canAddOutput:self.imageOutput]) {
		[self.captureSession addOutput:self.imageOutput];
	}

    // Setup movie file output
    self.movieOutput = [[AVCaptureMovieFileOutput alloc] init];             // 9

    if ([self.captureSession canAddOutput:self.movieOutput]) {
        [self.captureSession addOutput:self.movieOutput];
    }

    return YES;
}

- (void)startSession {
	if (![self.captureSession isRunning]) {                                 // 1
		dispatch_async([self globalQueue], ^{
			[self.captureSession startRunning];
		});
	}
}

- (void)stopSession {
	if ([self.captureSession isRunning]) {                                  // 2
		dispatch_async([self globalQueue], ^{
			[self.captureSession stopRunning];
		});
	}
}
6. 视频录制/拍照 + 滤镜 (CoreImage框架来实现滤镜效果)

+ (NSArray *)filterNames {
    
    return @[@"CIPhotoEffectChrome",
             @"CIPhotoEffectFade",
             @"CIPhotoEffectInstant",
             @"CIPhotoEffectMono",
             @"CIPhotoEffectNoir",
             @"CIPhotoEffectProcess",
             @"CIPhotoEffectTonal",
             @"CIPhotoEffectTransfer"];
}
            
CIFilter * fliter = [CIFilter filterWithName:name];

- (void)processSampleBuffer:(CMSampleBufferRef)sampleBuffer {
    
    if (!self.isWriting) {
        return;
    }
    
    CMFormatDescriptionRef formatDesc =                                     // 1
        CMSampleBufferGetFormatDescription(sampleBuffer);
    
    CMMediaType mediaType = CMFormatDescriptionGetMediaType(formatDesc);

    if (mediaType == kCMMediaType_Video) {

        CMTime timestamp =
            CMSampleBufferGetPresentationTimeStamp(sampleBuffer);
        
        if (self.firstSample) {                                             // 2
            if ([self.assetWriter startWriting]) {
                [self.assetWriter startSessionAtSourceTime:timestamp];
            } else {
                NSLog(@"Failed to start writing.");
            }
            self.firstSample = NO;
        }
        
        CVPixelBufferRef outputRenderBuffer = NULL;
        
        CVPixelBufferPoolRef pixelBufferPool =
            self.assetWriterInputPixelBufferAdaptor.pixelBufferPool;
        
        OSStatus err = CVPixelBufferPoolCreatePixelBuffer(NULL,             // 3
                                                          pixelBufferPool,
                                                          &outputRenderBuffer);
        if (err) {
            NSLog(@"Unable to obtain a pixel buffer from the pool.");
            return;
        }

        CVPixelBufferRef imageBuffer =                                      // 4
            CMSampleBufferGetImageBuffer(sampleBuffer);

        CIImage *sourceImage = [CIImage imageWithCVPixelBuffer:imageBuffer
                                                       options:nil];

        [self.activeFilter setValue:sourceImage forKey:kCIInputImageKey];

        CIImage *filteredImage = self.activeFilter.outputImage;

        if (!filteredImage) {
            filteredImage = sourceImage;
        }

        [self.ciContext render:filteredImage                                // 5
               toCVPixelBuffer:outputRenderBuffer
                        bounds:filteredImage.extent
                    colorSpace:self.colorSpace];


        if (self.assetWriterVideoInput.readyForMoreMediaData) {             // 6
            if (![self.assetWriterInputPixelBufferAdaptor
                            appendPixelBuffer:outputRenderBuffer
                         withPresentationTime:timestamp]) {
                NSLog(@"Error appending pixel buffer.");
            }
        }
        
        CVPixelBufferRelease(outputRenderBuffer);
        
    }
    else if (!self.firstSample && mediaType == kCMMediaType_Audio) {        // 7
        if (self.assetWriterAudioInput.isReadyForMoreMediaData) {
            if (![self.assetWriterAudioInput appendSampleBuffer:sampleBuffer]) {
                NSLog(@"Error appending audio sample buffer.");
            }
        }
    }

}

7. 短视频制作-

//

8.CoreAnimation 使用
CALayer *parentLayer = self.view.layer;
    
    UIImage *image = [UIImage imageNamed:@"lavf_cover"];
    
    CALayer *imageLayer = [CALayer layer];
    // Set the layer contents to the book cover image
    imageLayer.contents = (id)image.CGImage;
    imageLayer.contentsScale = [UIScreen mainScreen].scale;
    
    // Size and position the layer
    CGFloat midX = CGRectGetMidX(parentLayer.bounds);
    CGFloat midY = CGRectGetMidY(parentLayer.bounds);
    
    imageLayer.bounds = CGRectMake(0, 0, image.size.width, image.size.height);
    imageLayer.position = CGPointMake(midX, midY);
    
    // Add the image layer as a sublayer of the parent layer
    [parentLayer addSublayer:imageLayer];
    
    // Basic animation to rotate around z-axis
    CABasicAnimation *rotationAnimation =
        [CABasicAnimation animationWithKeyPath:@"transform.rotation.z"];
    
    // Rotate 360 degrees over a three-second duration, repeat indefinitely
    rotationAnimation.toValue = @(2 * M_PI);
    rotationAnimation.duration = 1.0f;
    rotationAnimation.repeatCount = HUGE_VALF;
    
    // Add and execute animation on the image layer
    [imageLayer addAnimation:rotationAnimation forKey:@"rotateAnimation"];

    
    CABasicAnimation *rotationAnimationx =
        [CABasicAnimation animationWithKeyPath:@"transform.rotation.x"];
    
    // Rotate 360 degrees over a three-second duration, repeat indefinitely
    rotationAnimationx.toValue = @(2 * M_PI);
    rotationAnimationx.duration = 2.0f;
    rotationAnimationx.repeatCount = HUGE_VALF;
    
    // Add and execute animation on the image layer
    [imageLayer addAnimation:rotationAnimationx forKey:@"rotateAnimation1"];

    CABasicAnimation *rotationAnimationy =
        [CABasicAnimation animationWithKeyPath:@"transform.rotation.y"];
    
    // Rotate 360 degrees over a three-second duration, repeat indefinitely
    rotationAnimationy.toValue = @(2 * M_PI);
    rotationAnimationy.duration = 3.0f;
    rotationAnimationy.repeatCount = HUGE_VALF;
    
    // Add and execute animation on the image layer
    [imageLayer addAnimation:rotationAnimationy forKey:@"rotateAnimationy"];

}