更新时间:2022-10-17 17:58:47
一些事情:
就音频而言,你正在添加一个视频(摄像头)输入,但没有音频输入。这样做是为了获得声音。
让audioInputDevice = AVCaptureDevice.defaultDeviceWithMediaType(AVMediaTypeAudio)
do {
let input = try AVCaptureDeviceInput(device:audioInputDevice)
if sourceAVFoundation.captureSession.canAddInput(input){
sourceAVFoundation.captureSession.addInput(input)
} else {
NSLog(错误:无法添加音频输入)
}
} catch let error {
NSLog(ERROR:获取输入设备:\(错误) )
}
要制作视频广场,你必须要看使用AVAssetWriter而不是AVCaptureFileOutput。这更复杂,但你会获得更多的力量。你已经创建了一个很好的AVCaptureSession来连接AssetWriter,你需要做这样的事情:
let fileManager = NSFileManager.defaultManager()
let urls = fileManager.URLsForDirectory(.DocumentDirectory,inDomains:.UserDomainMask)
guard let documentDirectory:NSURL = urls.first else {
print(视频控制器:getAssetWriter:documentDir错误)
返回nil
}
让local_video_name = NSUUID()。UUIDString +。mp4
self.videoOutputURL = documentDirectory .URLByAppendingPathComponent(local_video_name)
guard let url = self.videoOutputURL else {
return nil
}
self.assetWriter = try ? AVAssetWriter(URL:url,fileType:AVFileTypeMPEG4)
guard let writer = self.assetWriter else {
return nil
}
// TODO:在此处设置所需的视频大小!
让videoSettings:[字符串:AnyObject] = [
AVVideoCodecKey:AVVideoCodecH264,
AVVideoWidthKey:captureSize.width,
AVVideoHeightKey:captureSize.height,
AVVideoCompressionPropertiesKey:[
AVVideoAverageBitRateKey:200000,
AVVideoProfileLevelKey:AVVideoProfileLevelH264Baseline41,
AVVideoMaxKeyFrameIntervalKey:90,
],
]
assetWriterInputCamera = AVAssetWriterInput(mediaType:AVMediaTypeVideo, outputSettings:?!videoSettings)
assetWriterInputCamera .expectsMediaDataInRealTime =真
writer.addInput(assetWriterInputCamera)
让audioSettings:[字符串:AnyObject] = [
AVFormatIDKey:NSInteger的(kAudioFormatMPEG4AAC)
AVNumberOfChannelsKey:2,
AVSampleRateKey:NSNumber的(双:44100.0)
]
assetWriterInputAudio = AVAssetWriterInput(mediaType的:甲VMediaTypeAudio,outputSettings:audioSettings)
assetWriterInputAudio?。exppectsMediaDataInRealTime = true
writer.addInput(assetWriterInputAudio!)
一旦你设置了AssetWriter ......然后连接视频和音频的一些输出
让bufferAudioQueue = dispatch_queue_create( 音频缓冲器委托,DISPATCH_QUEUE_SERIAL)
让audioOutput = AVCaptureAudioDataOutput()
audioOutput.setSampleBufferDelegate(个体,队列:bufferAudioQueue)
captureSession.addOutput(audioOutput)
//始终添加视频...
let videoOutput = AVCaptureVideoDataOutput()
videoOutput.setSampleBufferDelegate(self,queue:bufferVideoQueue)
captureSession.addOutput(videoOutput)
如果let connection = videoOutput.connectionWithMediaType(AVMediaTypeVideo){
if connection.supportsVideoOrientation {
//强制录制到肖像
connection.videoOrientation = AVCaptureVideoOrientation.Portrait
}
self.outputConnection = connection
}
captureSession.startRunning()
最后你需要捕获缓冲区并处理这些东西...确保你的类成为AVCaptureVideoDataOutputSampleBufferDelegate的委托AVCaptureVideoDataOutputSampleBufferDelegate,AVCaptureAudioDataOutputSampleBufferDelegate
func captureOutput(captureOutput:AVCaptureOutput!,didOutputSampleBuffer sampleBuffer:) CMSampleBuffer!,fromConnection connection:AVCaptureConnection!){
if!self.isRecordingStarted {
return
}
if let audio = self.assetWriterInputAudio where connection.audioChannels.count> 0&& audio.readyForMoreMediaData {
dispatch_async(audioQueue!){
audio.appendSampleBuffer(sampleBuffer)
}
return
}
如果让camera = self.assetWriterInputCamera,其中camera.readyForMoreMediaData {
dispatch_async(videoQueue!){
camera.appendSampleBuffer(sampleBuffer)
}
}
}
有一些缺失的零碎,但希望这足以让你弄清楚它与文档。
最后,如果你想添加水印,有很多方法可以实时完成,但一种可能的方法是修改sampleBuffer并写然后水印成图像。你会在***上找到其他问题。
Illustration of what I'm trying to do
I'm trying to do the following:
Up to this point I managed to play the music, show the AVCaptureVideoPreviewLayer in a square container in a different view and save the video to the camera roll.
The thing is that I can barely find a few vague tutorials about using AVFoundation and this being my first app, makes things quite hard.
I managed to do these things, but I still don't understand how AVFoundation works. The documentation is vague for a beginner and I haven't found a tutorial for what I specifically want and putting together multiple tutorials (and written in Obj C) is making this impossible. My problems are the following:
So I make the preparations:
override func viewDidLoad() {
super.viewDidLoad()
// Preset For High Quality
captureSession.sessionPreset = AVCaptureSessionPresetHigh
// Get available devices capable of recording video
let devices = AVCaptureDevice.devicesWithMediaType(AVMediaTypeVideo) as! [AVCaptureDevice]
// Get back camera
for device in devices
{
if device.position == AVCaptureDevicePosition.Back
{
currentDevice = device
}
}
// Set Input
let captureDeviceInput: AVCaptureDeviceInput
do
{
captureDeviceInput = try AVCaptureDeviceInput(device: currentDevice)
}
catch
{
print(error)
return
}
// Set Output
videoFileOutput = AVCaptureMovieFileOutput()
// Configure Session w/ Input & Output Devices
captureSession.addInput(captureDeviceInput)
captureSession.addOutput(videoFileOutput)
// Show Camera Preview
cameraPreviewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
view.layer.addSublayer(cameraPreviewLayer!)
cameraPreviewLayer?.videoGravity = AVLayerVideoGravityResizeAspectFill
let width = view.bounds.width*0.85
cameraPreviewLayer?.frame = CGRectMake(0, 0, width, width)
// Bring Record Button To Front
view.bringSubviewToFront(recordButton)
captureSession.startRunning()
// // Bring Message To Front
// view.bringSubviewToFront(messageView)
// view.bringSubviewToFront(messageText)
// view.bringSubviewToFront(messageImage)
}
Then when I press the record button:
@IBAction func capture(sender: AnyObject) {
if !isRecording
{
isRecording = true
UIView.animateWithDuration(0.5, delay: 0.0, options: [.Repeat, .Autoreverse, .AllowUserInteraction], animations: { () -> Void in
self.recordButton.transform = CGAffineTransformMakeScale(0.5, 0.5)
}, completion: nil)
let outputPath = NSTemporaryDirectory() + "output.mov"
let outputFileURL = NSURL(fileURLWithPath: outputPath)
videoFileOutput?.startRecordingToOutputFileURL(outputFileURL, recordingDelegate: self)
}
else
{
isRecording = false
UIView.animateWithDuration(0.5, delay: 0, options: [], animations: { () -> Void in
self.recordButton.transform = CGAffineTransformMakeScale(1.0, 1.0)
}, completion: nil)
recordButton.layer.removeAllAnimations()
videoFileOutput?.stopRecording()
}
}
And after the video was recorded:
func captureOutput(captureOutput: AVCaptureFileOutput!, didFinishRecordingToOutputFileAtURL outputFileURL: NSURL!, fromConnections connections: [AnyObject]!, error: NSError!) {
let outputPath = NSTemporaryDirectory() + "output.mov"
if UIVideoAtPathIsCompatibleWithSavedPhotosAlbum(outputPath)
{
UISaveVideoAtPathToSavedPhotosAlbum(outputPath, self, nil, nil)
// Show Success Message
UIView.animateWithDuration(0.4, delay: 0, options: [], animations: {
self.messageView.alpha = 0.8
}, completion: nil)
UIView.animateWithDuration(0.4, delay: 0, options: [], animations: {
self.messageText.alpha = 1.0
}, completion: nil)
UIView.animateWithDuration(0.4, delay: 0, options: [], animations: {
self.messageImage.alpha = 1.0
}, completion: nil)
// Hide Message
UIView.animateWithDuration(0.4, delay: 1, options: [], animations: {
self.messageView.alpha = 0
}, completion: nil)
UIView.animateWithDuration(0.4, delay: 1, options: [], animations: {
self.messageText.alpha = 0
}, completion: nil)
UIView.animateWithDuration(0.4, delay: 1, options: [], animations: {
self.messageImage.alpha = 0
}, completion: nil)
}
}
So what do I need to do fix this? I kept searching and looking over tutorials but I can't figure it out... I read about adding watermarks and I saw that it has something to do with adding CALayers on top of the video. But obviously I can't do that since I don't even know how to make the video square and add audio.
A few things:
As far as Audio goes, you're adding a Video (camera) input, but no Audio input. So do that to get sound.
let audioInputDevice = AVCaptureDevice.defaultDeviceWithMediaType(AVMediaTypeAudio)
do {
let input = try AVCaptureDeviceInput(device: audioInputDevice)
if sourceAVFoundation.captureSession.canAddInput(input) {
sourceAVFoundation.captureSession.addInput(input)
} else {
NSLog("ERROR: Can't add audio input")
}
} catch let error {
NSLog("ERROR: Getting input device: \(error)")
}
To make the video square, you're going to have to look at using AVAssetWriter instead of AVCaptureFileOutput. This is more complex, but you get more "power". You've created an AVCaptureSession already which is great, to hook up the AssetWriter, you'll need to do something like this:
let fileManager = NSFileManager.defaultManager()
let urls = fileManager.URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)
guard let documentDirectory: NSURL = urls.first else {
print("Video Controller: getAssetWriter: documentDir Error")
return nil
}
let local_video_name = NSUUID().UUIDString + ".mp4"
self.videoOutputURL = documentDirectory.URLByAppendingPathComponent(local_video_name)
guard let url = self.videoOutputURL else {
return nil
}
self.assetWriter = try? AVAssetWriter(URL: url, fileType: AVFileTypeMPEG4)
guard let writer = self.assetWriter else {
return nil
}
//TODO: Set your desired video size here!
let videoSettings: [String : AnyObject] = [
AVVideoCodecKey : AVVideoCodecH264,
AVVideoWidthKey : captureSize.width,
AVVideoHeightKey : captureSize.height,
AVVideoCompressionPropertiesKey : [
AVVideoAverageBitRateKey : 200000,
AVVideoProfileLevelKey : AVVideoProfileLevelH264Baseline41,
AVVideoMaxKeyFrameIntervalKey : 90,
],
]
assetWriterInputCamera = AVAssetWriterInput(mediaType: AVMediaTypeVideo, outputSettings: videoSettings)
assetWriterInputCamera?.expectsMediaDataInRealTime = true
writer.addInput(assetWriterInputCamera!)
let audioSettings : [String : AnyObject] = [
AVFormatIDKey : NSInteger(kAudioFormatMPEG4AAC),
AVNumberOfChannelsKey : 2,
AVSampleRateKey : NSNumber(double: 44100.0)
]
assetWriterInputAudio = AVAssetWriterInput(mediaType: AVMediaTypeAudio, outputSettings: audioSettings)
assetWriterInputAudio?.expectsMediaDataInRealTime = true
writer.addInput(assetWriterInputAudio!)
Once you have the AssetWriter setup... then hook up some outputs for the Video and Audio
let bufferAudioQueue = dispatch_queue_create("audio buffer delegate", DISPATCH_QUEUE_SERIAL)
let audioOutput = AVCaptureAudioDataOutput()
audioOutput.setSampleBufferDelegate(self, queue: bufferAudioQueue)
captureSession.addOutput(audioOutput)
// Always add video last...
let videoOutput = AVCaptureVideoDataOutput()
videoOutput.setSampleBufferDelegate(self, queue: bufferVideoQueue)
captureSession.addOutput(videoOutput)
if let connection = videoOutput.connectionWithMediaType(AVMediaTypeVideo) {
if connection.supportsVideoOrientation {
// Force recording to portrait
connection.videoOrientation = AVCaptureVideoOrientation.Portrait
}
self.outputConnection = connection
}
captureSession.startRunning()
Finally you need to capture the buffers and process that stuff... Make sure you make your class a delegate of AVCaptureVideoDataOutputSampleBufferDelegate and AVCaptureAudioDataOutputSampleBufferDelegate
//MARK: Implementation for AVCaptureVideoDataOutputSampleBufferDelegate, AVCaptureAudioDataOutputSampleBufferDelegate
func captureOutput(captureOutput: AVCaptureOutput!, didOutputSampleBuffer sampleBuffer: CMSampleBuffer!, fromConnection connection: AVCaptureConnection!) {
if !self.isRecordingStarted {
return
}
if let audio = self.assetWriterInputAudio where connection.audioChannels.count > 0 && audio.readyForMoreMediaData {
dispatch_async(audioQueue!) {
audio.appendSampleBuffer(sampleBuffer)
}
return
}
if let camera = self.assetWriterInputCamera where camera.readyForMoreMediaData {
dispatch_async(videoQueue!) {
camera.appendSampleBuffer(sampleBuffer)
}
}
}
There are a few missing bits and pieces, but hopefully this is enough for you to figure it out along with the documentation.
Finally, if you want to add the watermark, there are many ways this can be done in real time, but one possible way is to modify the sampleBuffer and write the watermark into the image then. You'll find other question on *** dealing with that.