前言
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"];
}