водяной знак iPhone на записанном видео.
В моем приложении мне нужно захватить видео и поставить водяной знак на это видео. Водяным знаком должен быть текст(время и примечания). Я видел код, использующий фрейм" QTKit". Однако я читал, что фреймворк недоступен для iPhone.
Заранее благодарю.
5 ответов:
Используйте
AVFoundation
. Я бы предложил захватывать кадры с помощьюAVCaptureVideoDataOutput
, затем накладывать захваченный кадр на изображение водяного знака и, наконец, записывать захваченные и обработанные кадры в файл пользователяAVAssetWriter
.Поиск вокруг переполнения стека, есть тонна фантастических примеров, подробно описывающих, как сделать каждую из этих вещей, которые я упомянул. Я не видел ни одного, который давал бы примеры кода именно для того эффекта, который вы хотели бы, но вы должны быть в состоянии смешивать и сопоставлять довольно легко.
Правка:
Взгляните на эти ссылки:
IPhone: AVCaptureSession capture output crashing (AVCaptureVideoDataOutput) - этот пост может быть полезен просто по характеру содержания соответствующего кода.
AVCaptureDataOutput
вернет изображения в видеCMSampleBufferRef
s. Преобразуйте их вCGImageRef
s, используя следующий код:- (CGImageRef) imageFromSampleBuffer:(CMSampleBufferRef) sampleBuffer // Create a CGImageRef from sample buffer data { CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer); CVPixelBufferLockBaseAddress(imageBuffer,0); // Lock the image buffer uint8_t *baseAddress = (uint8_t *)CVPixelBufferGetBaseAddressOfPlane(imageBuffer, 0); // Get information of the image size_t bytesPerRow = CVPixelBufferGetBytesPerRow(imageBuffer); size_t width = CVPixelBufferGetWidth(imageBuffer); size_t height = CVPixelBufferGetHeight(imageBuffer); CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB(); CGContextRef newContext = CGBitmapContextCreate(baseAddress, width, height, 8, bytesPerRow, colorSpace, kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedFirst); CGImageRef newImage = CGBitmapContextCreateImage(newContext); CGContextRelease(newContext); CGColorSpaceRelease(colorSpace); CVPixelBufferUnlockBaseAddress(imageBuffer,0); /* CVBufferRelease(imageBuffer); */ // do not call this! return newImage; }
Оттуда вы бы преобразовались в UIImage,
UIImage *img = [UIImage imageWithCGImage:yourCGImage];
Затем используйте
[img drawInRect:CGRectMake(x,y,height,width)];
Чтобы нарисовать рамку в контексте, нарисуйте PNG нанесите на него водяной знак, а затем добавьте обработанные изображения к выходному видео с помощью
AVAssetWriter
. Я бы предложил добавлять их в реальном времени, чтобы вы не заполняли память тоннами изображений UI.Как экспортировать массив UIImage в виде фильма? - в этом посте показано, как добавить обработанные UI-изображения в видео заданной длительности.
Это должно помочь вам на вашем пути к водяным знакам ваших видео. Не забывайте практиковать хорошее управление памятью, потому что утечка изображений, которые приходят в 20-30fps это отличный способ, чтобы разбить приложение.
Добавление водяного знака гораздо проще. Вам просто нужно использовать CALayer и AVVideoCompositionCoreAnimationTool. Код можно просто скопировать и собрать в том же порядке. Я только что попытался вставить некоторые комментарии между ними для лучшего понимания.
Предположим, что вы уже записали видео, поэтому сначала мы создадим AVURLAsset:
AVURLAsset* videoAsset = [[AVURLAsset alloc]initWithURL:outputFileURL options:nil]; AVMutableComposition* mixComposition = [AVMutableComposition composition]; AVMutableCompositionTrack *compositionVideoTrack = [mixComposition addMutableTrackWithMediaType:AVMediaTypeVideo preferredTrackID:kCMPersistentTrackID_Invalid]; AVAssetTrack *clipVideoTrack = [[videoAsset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0]; [compositionVideoTrack insertTimeRange:CMTimeRangeMake(kCMTimeZero, videoAsset.duration) ofTrack:clipVideoTrack atTime:kCMTimeZero error:nil]; [compositionVideoTrack setPreferredTransform:[[[videoAsset tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0] preferredTransform]];
С помощью этого кода Вы сможете экспортировать видео, но Сначала мы хотим добавить слой с водяным знаком. Обратите внимание, что некоторые коды могут показаться избыточными, но они необходимы для того, чтобы все работало.
Сначала создадим слой с изображением водяного знака:
UIImage *myImage = [UIImage imageNamed:@"icon.png"]; CALayer *aLayer = [CALayer layer]; aLayer.contents = (id)myImage.CGImage; aLayer.frame = CGRectMake(5, 25, 57, 57); //Needed for proper display. We are using the app icon (57x57). If you use 0,0 you will not see it aLayer.opacity = 0.65; //Feel free to alter the alpha here
Если нам не нужно изображение, а нужен текст:
CATextLayer *titleLayer = [CATextLayer layer]; titleLayer.string = @"Text goes here"; titleLayer.font = @"Helvetica"; titleLayer.fontSize = videoSize.height / 6; //?? titleLayer.shadowOpacity = 0.5; titleLayer.alignmentMode = kCAAlignmentCenter; titleLayer.bounds = CGRectMake(0, 0, videoSize.width, videoSize.height / 6); //You may need to adjust this for proper display
Следующий код сортирует слой в правильном порядке:
CGSize videoSize = [videoAsset naturalSize]; CALayer *parentLayer = [CALayer layer]; CALayer *videoLayer = [CALayer layer]; parentLayer.frame = CGRectMake(0, 0, videoSize.width, videoSize.height); videoLayer.frame = CGRectMake(0, 0, videoSize.width, videoSize.height); [parentLayer addSublayer:videoLayer]; [parentLayer addSublayer:aLayer]; [parentLayer addSublayer:titleLayer]; //ONLY IF WE ADDED TEXT
Теперь мы создаем композицию и добавляем инструкции для вставки слоя:
AVMutableVideoComposition* videoComp = [[AVMutableVideoComposition videoComposition] retain]; videoComp.renderSize = videoSize; videoComp.frameDuration = CMTimeMake(1, 30); videoComp.animationTool = [AVVideoCompositionCoreAnimationTool videoCompositionCoreAnimationToolWithPostProcessingAsVideoLayer:videoLayer inLayer:parentLayer]; /// instruction AVMutableVideoCompositionInstruction *instruction = [AVMutableVideoCompositionInstruction videoCompositionInstruction]; instruction.timeRange = CMTimeRangeMake(kCMTimeZero, [mixComposition duration]); AVAssetTrack *videoTrack = [[mixComposition tracksWithMediaType:AVMediaTypeVideo] objectAtIndex:0]; AVMutableVideoCompositionLayerInstruction* layerInstruction = [AVMutableVideoCompositionLayerInstruction videoCompositionLayerInstructionWithAssetTrack:videoTrack]; instruction.layerInstructions = [NSArray arrayWithObject:layerInstruction]; videoComp.instructions = [NSArray arrayWithObject: instruction];
И теперь мы готовы экспортировать:
_assetExport = [[AVAssetExportSession alloc] initWithAsset:mixComposition presetName:AVAssetExportPresetMediumQuality];//AVAssetExportPresetPassthrough _assetExport.videoComposition = videoComp; NSString* videoName = @"mynewwatermarkedvideo.mov"; NSString *exportPath = [NSTemporaryDirectory() stringByAppendingPathComponent:videoName]; NSURL *exportUrl = [NSURL fileURLWithPath:exportPath]; if ([[NSFileManager defaultManager] fileExistsAtPath:exportPath]) { [[NSFileManager defaultManager] removeItemAtPath:exportPath error:nil]; } _assetExport.outputFileType = AVFileTypeQuickTimeMovie; _assetExport.outputURL = exportUrl; _assetExport.shouldOptimizeForNetworkUse = YES; [strRecordedFilename setString: exportPath]; [_assetExport exportAsynchronouslyWithCompletionHandler: ^(void ) { [_assetExport release]; //YOUR FINALIZATION CODE HERE } ]; [audioAsset release]; [videoAsset release];
Уже ответ, данный @Julio, прекрасно работает в случае objective-c Вот та же кодовая база для Swift 3.0:
Водяной знак и создание квадратного или обрезанного видео, как Instagram
Получение выходного файла из каталога Documents и создание AVURLAsset
//output file let documentsURL = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first let outputPath = documentsURL?.appendingPathComponent("squareVideo.mov") if FileManager.default.fileExists(atPath: (outputPath?.path)!) { do { try FileManager.default.removeItem(atPath: (outputPath?.path)!) } catch { print ("Error deleting file") } } //input file let asset = AVAsset.init(url: filePath) print (asset) let composition = AVMutableComposition.init() composition.addMutableTrack(withMediaType: AVMediaTypeVideo, preferredTrackID: kCMPersistentTrackID_Invalid) //input clip let clipVideoTrack = asset.tracks(withMediaType: AVMediaTypeVideo)[0]
Создайте слой с изображением водяного знака:
//adding the image layer let imglogo = UIImage(named: "video_button") let watermarkLayer = CALayer() watermarkLayer.contents = imglogo?.cgImage watermarkLayer.frame = CGRect(x: 5, y: 25 ,width: 57, height: 57) watermarkLayer.opacity = 0.85
Создайте слой с текстом в качестве водяного знака вместо изображения:
let textLayer = CATextLayer() textLayer.string = "Nodat" textLayer.foregroundColor = UIColor.red.cgColor textLayer.font = UIFont.systemFont(ofSize: 50) textLayer.alignmentMode = kCAAlignmentCenter textLayer.bounds = CGRect(x: 5, y: 25, width: 100, height: 20)
Тот самый следующий код сортирует слой в правильном порядке:
let videoSize = clipVideoTrack.naturalSize let parentlayer = CALayer() let videoLayer = CALayer() parentlayer.frame = CGRect(x: 0, y: 0, width: videoSize.height, height: videoSize.height) videoLayer.frame = CGRect(x: 0, y: 0, width: videoSize.height, height: videoSize.height) parentlayer.addSublayer(videoLayer) parentlayer.addSublayer(watermarkLayer) parentlayer.addSublayer(textLayer) //for text layer only
Добавление слоев поверх видео в правильном порядке для водяного знака
let videoSize = clipVideoTrack.naturalSize let parentlayer = CALayer() let videoLayer = CALayer() parentlayer.frame = CGRect(x: 0, y: 0, width: videoSize.height, height: videoSize.height) videoLayer.frame = CGRect(x: 0, y: 0, width: videoSize.height, height: videoSize.height) parentlayer.addSublayer(videoLayer) parentlayer.addSublayer(watermarkLayer) parentlayer.addSublayer(textLayer) //for text layer only
Обрезка видео в квадратном формате-размером 300*300
//make it square let videoComposition = AVMutableVideoComposition() videoComposition.renderSize = CGSize(width: 300, height: 300) //change it as per your needs. videoComposition.frameDuration = CMTimeMake(1, 30) videoComposition.renderScale = 1.0 //Magic line for adding watermark to the video videoComposition.animationTool = AVVideoCompositionCoreAnimationTool(postProcessingAsVideoLayers: [videoLayer], in: parentlayer) let instruction = AVMutableVideoCompositionInstruction() instruction.timeRange = CMTimeRangeMake(kCMTimeZero, CMTimeMakeWithSeconds(60, 30))
Поворот к портрету
//rotate to potrait let transformer = AVMutableVideoCompositionLayerInstruction(assetTrack: clipVideoTrack) let t1 = CGAffineTransform(translationX: clipVideoTrack.naturalSize.height, y: -(clipVideoTrack.naturalSize.width - clipVideoTrack.naturalSize.height) / 2) let t2: CGAffineTransform = t1.rotated(by: .pi/2) let finalTransform: CGAffineTransform = t2 transformer.setTransform(finalTransform, at: kCMTimeZero) instruction.layerInstructions = [transformer] videoComposition.instructions = [instruction]
Последний шаг для экспорта видео
let exporter = AVAssetExportSession.init(asset: asset, presetName: AVAssetExportPresetMediumQuality) exporter?.outputFileType = AVFileTypeQuickTimeMovie exporter?.outputURL = outputPath exporter?.videoComposition = videoComposition exporter?.exportAsynchronously() { handler -> Void in if exporter?.status == .completed { print("Export complete") DispatchQueue.main.async(execute: { completion(outputPath) }) return } else if exporter?.status == .failed { print("Export failed - \(String(describing: exporter?.error))") } completion(nil) return }
Это позволит экспортировать видео в квадратном размере с водяным знаком в виде текста или изображения
Спасибо
Вот пример на swift3, Как вставить в записанное видео как анимированные (массив изображений/слайдов/кадров), так и статические водяные знаки изображения.
Он использует CAKeyframeAnimation для анимации кадров, и он использует AVMutableCompositionTrack, AVAssetExportSession и AVMutableVideoComposition вместе с AVMutableVideoCompositionInstruction, чтобы объединить все вместе.