водяной знак iPhone на записанном видео.
в моем приложении мне нужно снять видео и поставить водяной знак на видео. Водяным знаком должен быть текст (время и заметки). Я видел код, использующий фрейм "QTKit". Однако я читал, что фреймворк недоступен для iPhone.
спасибо заранее.
5 ответов
использовать AVFoundation
. Я бы предложил захватить кадры с AVCaptureVideoDataOutput
, затем наложение захваченного кадра с изображением водяного знака и, наконец, запись захваченных и обработанных кадров в файл пользователя AVAssetWriter
.
Поиск вокруг переполнения стека, есть тонна фантастических примеров, подробно описывающих, как сделать каждую из этих вещей, которые я упомянул. Я не видел примеров кода, которые дают именно тот эффект, который вы хотели бы, но вы должны уметь смешивать и сопоставлять довольно легко.
EDIT:
взгляните на эти ссылки:
iPhone: AVCaptureSession захвата выход сбой (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
. Я бы предложил добавить их в режиме реального времени, чтобы вы не заполняли память тоннами UIImages.
как экспортировать массив UIImage в виде фильма? - это сообщение показывает, как добавить uiimages, которые вы обработали в видео для заданной продолжительности.
это должно помочь вам на вашем пути для водяных знаков ваших видео. Не забудьте попрактиковаться в хорошем управлении памятью, потому что утечка изображений, которые поступают в 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
получение выходного файла из каталога документов и создание 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 объединить все вместе.