Tôi đang cố gắng kết hợp một video với một hình ảnh. Đây không phải là cố gắng để kết hợp nhiều hình ảnh vào một video duy nhất nhưCách hợp nhất * Hình ảnh đơn * với video
Tôi đang sử dụng AVMutableComposition
để kết hợp các bài hát . Ứng dụng của tôi có khả năng kết hợp video và hình ảnh (nhưng khi nó đứng, các video kết hợp là tốt!) Tôi cố gắng sử dụng AVAssetWriter
để biến một hình ảnh thành video (tôi tin rằng đây là vấn đề của tôi nhưng không chắc chắn 100%). Sau đó, tôi lưu nó vào ứng dụng (documents directory
). Từ đó, tôi truy cập vào bên trong sự hợp nhất của mình và kết hợp video và hình ảnh hiện đã chuyển thành video.
luồng:
tài chọn hình ảnh ->
ảnhvào AVAssetWriter chuyển sang video ->
Merge một đoạn video tôi đã có cài sẵn với video ->
Kết quả: Tạo 1 video từ hình ảnh đã chọn và video cài sẵn.
Sự cố với những gì tôi có: Mã của tôi đang cung cấp một khoảng trống nơi hình ảnh bên trong video sẽ hiển thị. Như trong tập tin ImageConverter tôi có, sẽ chuyển nó thành video, nhưng tôi sẽ chỉ xem khung hình LAST như hình ảnh, trong khi mọi khung hình khác là trong suốt, như thể hình ảnh không có ở đó. Vì vậy, nếu tôi chuyển đổi hình ảnh thành video trong 5 giây (giả sử ở 30 khung hình/giây) thì tôi sẽ thấy khoảng trống cho (30 * 5) -1 khung và sau đó là khung cuối cùng, hình ảnh cuối cùng sẽ xuất hiện. Tôi chỉ đang tìm kiếm hướng dẫn về cách tạo một hình ảnh đơn lẻ thành video HOẶC kết hợp video và hình ảnh với nhau KHÔNG chuyển đổi hình ảnh thành video. Cảm ơn!
Merge tập tin ở đây
func merge() {
if let firstAsset = controller.firstAsset, secondAsset = self.asset {
// 1 - Create AVMutableComposition object. This object will hold your AVMutableCompositionTrack instances.
let mixComposition = AVMutableComposition()
let firstTrack = mixComposition.addMutableTrackWithMediaType(AVMediaTypeVideo,
preferredTrackID: Int32(kCMPersistentTrackID_Invalid))
do {
try firstTrack.insertTimeRange(CMTimeRangeMake(kCMTimeZero, CMTime(seconds: 8, preferredTimescale: 600)),
ofTrack: firstAsset.tracksWithMediaType(AVMediaTypeVideo)[0] ,
atTime: kCMTimeZero)
} catch _ {
print("Failed to load first track")
}
do {
//HERE THE TIME IS 0.666667, BUT SHOULD BE 0
print(CMTimeGetSeconds(secondAsset.duration), CMTimeGetSeconds(firstTrack.timeRange.duration))
try firstTrack.insertTimeRange(CMTimeRangeMake(kCMTimeZero, secondAsset.duration),
ofTrack: secondAsset.tracksWithMediaType(AVMediaTypeVideo)[0],
atTime: firstTrack.timeRange.duration)
} catch _ {
print("Failed to load second track")
}
do {
try firstTrack.insertTimeRange(CMTimeRangeMake(CMTime(seconds: 8+CMTimeGetSeconds(secondAsset.duration), preferredTimescale: 600), firstAsset.duration),
ofTrack: firstAsset.tracksWithMediaType(AVMediaTypeVideo)[0] ,
atTime: firstTrack.timeRange.duration+secondTrack.timeRange.duration)
} catch _ {
print("failed")
}
// 3 - Audio track
if let loadedAudioAsset = controller.audioAsset {
let audioTrack = mixComposition.addMutableTrackWithMediaType(AVMediaTypeAudio, preferredTrackID: 0)
do {
try audioTrack.insertTimeRange(CMTimeRangeMake(kCMTimeZero, firstAsset.duration),
ofTrack: loadedAudioAsset.tracksWithMediaType(AVMediaTypeAudio)[0] ,
atTime: kCMTimeZero)
} catch _ {
print("Failed to load Audio track")
}
}
// 4 - Get path
let documentDirectory = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0]
let dateFormatter = NSDateFormatter()
dateFormatter.dateStyle = .LongStyle
dateFormatter.timeStyle = .ShortStyle
let date = dateFormatter.stringFromDate(NSDate())
let savePath = (documentDirectory as NSString).stringByAppendingPathComponent("mergeVideo.mov")
let url = NSURL(fileURLWithPath: savePath)
_ = try? NSFileManager().removeItemAtURL(url)
// 5 - Create Exporter
print("exporting")
guard let exporter = AVAssetExportSession(asset: mixComposition, presetName: AVAssetExportPresetHighestQuality) else { return }
exporter.outputURL = url
exporter.outputFileType = AVFileTypeQuickTimeMovie
exporter.shouldOptimizeForNetworkUse = false
exporter.videoComposition = mainComposition
// 6 - Perform the Export
controller.currentlyEditing = true
exporter.exportAsynchronouslyWithCompletionHandler() {
dispatch_async(dispatch_get_main_queue()) { _ in
print("done")
self.controller.currentlyEditing = false
self.controller.merged = true
self.button.blurView.superview?.hidden = true
self.controller.player.replaceCurrentItemWithPlayerItem(AVPlayerItem(URL: url))
self.controller.firstAsset = AVAsset(URL: url)
}
}
}
}
func exportDidFinish(session: AVAssetExportSession) {
if session.status == AVAssetExportSessionStatus.Failed {
print(session.error)
}
if session.status == AVAssetExportSessionStatus.Completed {
print("succed")
}
}
Chuyển đổi hình ảnh Dưới đây
class MyConverter: NSObject {
var image:UIImage!
convenience init(image:UIImage) {
self.init()
self.image = image
}
var outputURL: NSURL {
let documentDirectory = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0]
let savePath = (documentDirectory as NSString).stringByAppendingPathComponent("mergeVideo-pic.mov")
return getURL(savePath)
}
func getURL(path:String) -> NSURL {
let movieDestinationUrl = NSURL(fileURLWithPath: path)
_ = try? NSFileManager().removeItemAtURL(movieDestinationUrl)
let url = NSURL(fileURLWithPath: path)
return url
}
func build(completion:() -> Void) {
guard let videoWriter = try? AVAssetWriter(URL: outputURL, fileType: AVFileTypeQuickTimeMovie) else {
fatalError("AVAssetWriter error")
}
let outputSettings = [AVVideoCodecKey : AVVideoCodecH264, AVVideoWidthKey : NSNumber(float: Float(image.size.width)), AVVideoHeightKey : NSNumber(float: Float(image.size.height))]
guard videoWriter.canApplyOutputSettings(outputSettings, forMediaType: AVMediaTypeVideo) else {
fatalError("Negative : Can't apply the Output settings...")
}
let videoWriterInput = AVAssetWriterInput(mediaType: AVMediaTypeVideo, outputSettings: outputSettings)
let sourcePixelBufferAttributesDictionary = [kCVPixelBufferPixelFormatTypeKey as String : NSNumber(unsignedInt: kCVPixelFormatType_32ARGB), kCVPixelBufferWidthKey as String: NSNumber(float: Float(image.size.width)), kCVPixelBufferHeightKey as String: NSNumber(float: Float(image.size.height))]
let pixelBufferAdaptor = AVAssetWriterInputPixelBufferAdaptor(assetWriterInput: videoWriterInput, sourcePixelBufferAttributes: sourcePixelBufferAttributesDictionary)
if videoWriter.canAddInput(videoWriterInput) {
videoWriter.addInput(videoWriterInput)
}
if videoWriter.startWriting() {
videoWriter.startSessionAtSourceTime(kCMTimeZero)
assert(pixelBufferAdaptor.pixelBufferPool != nil)
}
let media_queue = dispatch_queue_create("mediaInputQueue", nil)
videoWriterInput.requestMediaDataWhenReadyOnQueue(media_queue, usingBlock: {() -> Void in
var appendSucceeded = true
//Time HERE IS ZERO, but in Merge file, it is 0.66667
let presentationTime = CMTimeMake(0, 600)
var pixelBuffer: CVPixelBuffer? = nil
let status: CVReturn = CVPixelBufferPoolCreatePixelBuffer(kCFAllocatorDefault, pixelBufferAdaptor.pixelBufferPool!, &pixelBuffer)
if let pixelBuffer = pixelBuffer where status == 0 {
let managedPixelBuffer = pixelBuffer
CVPixelBufferLockBaseAddress(managedPixelBuffer, 0)
let data = CVPixelBufferGetBaseAddress(managedPixelBuffer)
let rgbColorSpace = CGColorSpaceCreateDeviceRGB()
let context = CGBitmapContextCreate(data, Int(self.image.size.width), Int(self.image.size.height), 8, CVPixelBufferGetBytesPerRow(managedPixelBuffer), rgbColorSpace, CGImageAlphaInfo.PremultipliedFirst.rawValue)
CGContextClearRect(context, CGRectMake(0, 0, CGFloat(self.image.size.width), CGFloat(self.image.size.height)))
CGContextDrawImage(context, CGRectMake(0, 0, self.image.size.width, self.image.size.height), self.image.CGImage)
CVPixelBufferUnlockBaseAddress(managedPixelBuffer, 0)
appendSucceeded = pixelBufferAdaptor.appendPixelBuffer(pixelBuffer, withPresentationTime: presentationTime)
} else {
print("Failed to allocate pixel buffer")
appendSucceeded = false
}
if !appendSucceeded {
print("append failed")
}
videoWriterInput.markAsFinished()
videoWriter.finishWritingWithCompletionHandler {() -> Void in
print("FINISHED!!!!!")
completion()
}
})
}
}
Lưu ý: Tôi đã phát hiện ra rằng nếu tôi làm một print(presentationTime)
INSIDE ImageConverter nó in 0, và sau đó in thời gian của thời lượng bên trong sáp nhập, tôi nhận được 0,666667
Lưu ý: Không có câu trả lời nào được nêu ra, nhưng tôi sẽ liên tục đặt câu hỏi này thành tiền thưởng cho đến khi tôi tìm thấy câu trả lời hoặc người khác giúp tôi! Cảm ơn!
Tôi hoàn toàn biết ơn sự cố của bạn về vấn đề của tôi với sự kết hợp mã Obj-C và Swift của bạn. Tôi đã không thể kiểm tra mã này, nhưng vì tôi chỉ còn lại 15 giờ và bạn đưa ra câu trả lời sâu sắc nhất, tôi sẽ chọn bạn. Tôi đánh giá cao sự giúp đỡ của bạn! Cảm ơn bạn rất nhiều! – impression7vx
Điều này làm việc tuyệt vời! Nhưng bạn có thể giải thích tại sao tôi phải thêm vào thời điểm Zero và cuối cùng không? Nó có ý nghĩa, nhưng tại sao tôi không thể chỉ cần thêm một cái gì đó vớiPresentationTime? – impression7vx
Tôi không hoàn toàn chắc chắn. Đoán tốt nhất của tôi là nội bộ cách nó hoạt động là nó sẽ hiển thị một khung cho đến khi nó có một khung mới để thay thế nó. Vì vậy, bạn phải thêm nó vào đầu để làm cho nó hiển thị ngay lập tức, và sau đó bạn phải thêm nó vào thời gian trình bày để báo hiệu kết thúc (vì vậy nó biết bao lâu để hiển thị khung đầu tiên). Nếu bạn chỉ thêm một khung tại thời điểm trình bày, nó sẽ chỉ hiển thị cho đến khi video kết thúc và không có gì trước đó. – gadu