栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 面试经验 > 面试问答

仅播放AVMutableComposition()的第一首曲目

面试问答 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

仅播放AVMutableComposition()的第一首曲目

好的,所以对于我的确切问题,我必须

CGAffineTransform
在Swift中应用特定的转换才能获得所需的特定结果。我正在发布的当前作品适用于拍摄/获取的任何照片以及视频

//This method gets the orientation of the current transform. This method is used below to determine the orientationfunc orientationFromTransform(_ transform: CGAffineTransform) -> (orientation: UIImageOrientation, isPortrait: Bool) {    var assetOrientation = UIImageOrientation.up    var isPortrait = false    if transform.a == 0 && transform.b == 1.0 && transform.c == -1.0 && transform.d == 0 {        assetOrientation = .right        isPortrait = true    } else if transform.a == 0 && transform.b == -1.0 && transform.c == 1.0 && transform.d == 0 {        assetOrientation = .left        isPortrait = true    } else if transform.a == 1.0 && transform.b == 0 && transform.c == 0 && transform.d == 1.0 {        assetOrientation = .up    } else if transform.a == -1.0 && transform.b == 0 && transform.c == 0 && transform.d == -1.0 {        assetOrientation = .down    }    //Returns the orientation as a variable    return (assetOrientation, isPortrait)}//Method that lays out the instructions for each track I am editing and does the transformation on each individual track to get it lined up properlyfunc videoCompositionInstructionForTrack(_ track: AVCompositionTrack, _ asset: AVAsset) -> AVMutableVideoCompositionLayerInstruction {    //This method Returns set of instructions from the initial track    //Create inital instruction    let instruction = AVMutableVideoCompositionLayerInstruction(assetTrack: track)    //This is whatever asset you are about to apply instructions to.    let assetTrack = asset.tracks(withMediaType: AVMediaTypeVideo)[0]    //Get the original transform of the asset    var transform = assetTrack.preferredTransform    //Get the orientation of the asset and determine if it is in portrait or landscape - I forget which, but either if you take a picture or get in the camera roll it is ALWAYS determined as landscape at first, I don't recall which one. This method accounts for it.    let assetInfo = orientationFromTransform(transform)    //You need a little background to understand this part.         let width = MyAsset.tracks(withMediaType: AVMediaTypeVideo)[0].naturalSize.width/assetTrack.naturalSize.width    var height = MyAsset.tracks(withMediaType: AVMediaTypeVideo)[0].naturalSize.height/assetTrack.naturalSize.height    //If it is in portrait    if assetInfo.isPortrait {        //We actually change the height variable to divide by the width of the old asset instead of the height. This is because of the flip since we determined it is portrait and not landscape.         height = MyAsset.tracks(withMediaType: AVMediaTypeVideo)[0].naturalSize.height/assetTrack.naturalSize.width        //We apply the transform and scale the image appropriately.        transform = transform.scaledBy(x: height, y: height)        //We also have to move the image or video appropriately. Since we scaled it, it could be wayy off on the side, outside the bounds of the viewing.        let movement = ((1/height)*assetTrack.naturalSize.height)-assetTrack.naturalSize.height        //This lines it up dead center on the left side of the screen perfectly. Now we want to center it.        transform = transform.translatedBy(x: 0, y: movement)        //This calculates how much black there is. Cut it in half and there you go!        let totalBlackDistance = MyAsset.tracks(withMediaType: AVMediaTypeVideo)[0].naturalSize.width-transform.tx        transform = transform.translatedBy(x: 0, y: -(totalBlackDistance/2)*(1/height))    } else {        //Landscape! We don't need to change the variables, it is all defaulted that way (iOS prefers landscape items), so we scale it appropriately.        transform = transform.scaledBy(x: width, y: height)        //This is a little complicated haha. So because it is in landscape, the asset fits the height correctly, for me anyway; It was just extra long. Think of this as a ratio. I forgot exactly how I thought this through, but the end product looked like: Answer = ((Original height/current asset height)*(current asset width))/(Original width)        let scale:CGFloat = ((MyAsset.tracks(withMediaType: AVMediaTypeVideo)[0].naturalSize.height/assetTrack.naturalSize.height)*(assetTrack.naturalSize.width))/MyAsset.tracks(withMediaType: AVMediaTypeVideo)[0].naturalSize.width        transform = transform.scaledBy(x: scale, y: 1)        //The asset can be way off the screen again, so we have to move it back. This time we can have it dead center in the middle, because it wasn't backwards because it wasn't flipped because it was landscape. Again, another long complicated algorithm I derived.        let movement = ((MyAsset.tracks(withMediaType: AVMediaTypeVideo)[0].naturalSize.width-((MyAsset.tracks(withMediaType: AVMediaTypeVideo)[0].naturalSize.height/assetTrack.naturalSize.height)*(assetTrack.naturalSize.width)))/2)*(1/MyAsset.tracks(withMediaType: AVMediaTypeVideo)[0].naturalSize.height/assetTrack.naturalSize.height)        transform = transform.translatedBy(x: movement, y: 0)    }    //This creates the instruction and returns it so we can apply it to each individual track.    instruction.setTransform(transform, at: kCMTimeZero)    return instruction}

现在有了这些方法,我们现在可以对资产适当地应用正确和适当的转换,并使所有内容都变得干净整洁。

func merge() {if let firstAsset = MyAsset, let newAsset = newAsset {        //This creates our overall composition, our new video framework        let mixComposition = AVMutableComposition()        //One by one you create tracks (could use loop, but I just had 3 cases)        let firstTrack = mixComposition.addMutableTrack(withMediaType: AVMediaTypeVideo,   preferredTrackID: Int32(kCMPersistentTrackID_Invalid))        //You have to use a try, so need a do        do { //Inserting a timerange into a track. I already calculated my time, I call it startTime. This is where you would put your time. The preferredTimeScale doesn't have to be 600000 haha, I was playing with those numbers. It just allows precision. At is not where it begins within this individual track, but where it starts as a whole. As you notice below my At times are different You also need to give it which track  try firstTrack.insertTimeRange(CMTimeRangeMake(kCMTimeZero, CMTime(seconds: CMTimeGetSeconds(startTime), preferredTimescale: 600000)),          of: firstAsset.tracks(withMediaType: AVMediaTypeVideo)[0],          at: kCMTimeZero)        } catch _ { print("Failed to load first track")        }        //Create the 2nd track        let secondTrack = mixComposition.addMutableTrack(withMediaType: AVMediaTypeVideo,    preferredTrackID: Int32(kCMPersistentTrackID_Invalid))        do { //Apply the 2nd timeRange you have. Also apply the correct track you want try secondTrack.insertTimeRange(CMTimeRangeMake(kCMTimeZero, self.endTime-self.startTime),          of: newAsset.tracks(withMediaType: AVMediaTypeVideo)[0],          at: CMTime(seconds: CMTimeGetSeconds(startTime), preferredTimescale: 600000)) secondTrack.preferredTransform = newAsset.preferredTransform        } catch _ { print("Failed to load second track")        }        //We are not sure we are going to use the third track in my case, because they can edit to the end of the original video, causing us not to use a third track. But if we do, it is the same as the others!        var thirdTrack:AVMutableCompositionTrack!        if(self.endTime != controller.realDuration) { thirdTrack = mixComposition.addMutableTrack(withMediaType: AVMediaTypeVideo,    preferredTrackID: Int32(kCMPersistentTrackID_Invalid))        //This part appears again, at endTime which is right after the 2nd track is suppose to end. do {     try thirdTrack.insertTimeRange(CMTimeRangeMake(CMTime(seconds: CMTimeGetSeconds(endTime), preferredTimescale: 600000), self.controller.realDuration-endTime),          of: firstAsset.tracks(withMediaType: AVMediaTypeVideo)[0] ,          at: CMTime(seconds: CMTimeGetSeconds(endTime), preferredTimescale: 600000)) } catch _ {     print("failed") }        }        //Same thing with audio!        if let loadedAudioAsset = controller.audioAsset { let audioTrack = mixComposition.addMutableTrack(withMediaType: AVMediaTypeAudio, preferredTrackID: 0) do {     try audioTrack.insertTimeRange(CMTimeRangeMake(kCMTimeZero, self.controller.realDuration),   of: loadedAudioAsset.tracks(withMediaType: AVMediaTypeAudio)[0] ,   at: kCMTimeZero) } catch _ {     print("Failed to load Audio track") }        }        //So, now that we have all of these tracks we need to apply those instructions! If we don't, then they could be different sizes. Say my newAsset is 720x1080 and MyAsset is 1440x900 (These are just examples haha), then it would look a tad funky and possibly not show our new asset at all.        let mainInstruction = AVMutableVideoCompositionInstruction()        //Make sure the overall time range matches that of the individual tracks, if not, it could cause errors.         mainInstruction.timeRange = CMTimeRangeMake(kCMTimeZero, self.controller.realDuration)        //For each track we made, we need an instruction. Could set loop or do individually as such.        let firstInstruction = videoCompositionInstructionForTrack(firstTrack, firstAsset)        //You know, not 100% why this is here. This is 1 thing I did not look into well enough or understand enough to describe to you.         firstInstruction.setOpacity(0.0, at: startTime)        //Next Instruction        let secondInstruction = videoCompositionInstructionForTrack(secondTrack, self.asset)        //Again, not sure we need 3rd one, but if we do.        var thirdInstruction:AVMutableVideoCompositionLayerInstruction!        if(self.endTime != self.controller.realDuration) { secondInstruction.setOpacity(0.0, at: endTime) thirdInstruction = videoCompositionInstructionForTrack(thirdTrack, firstAsset)        }        //Okay, now that we have all these instructions, we tie them into the main instruction we created above.        mainInstruction.layerInstructions = [firstInstruction, secondInstruction]        if(self.endTime != self.controller.realDuration) { mainInstruction.layerInstructions += [thirdInstruction]        }        //We create a video framework now, slightly different than the one above.        let mainComposition = AVMutableVideoComposition()        //We apply these instructions to the framework        mainComposition.instructions = [mainInstruction]        //How long are our frames, you can change this as necessary        mainComposition.frameDuration = CMTimeMake(1, 30)        //This is your render size of the video. 720p, 1080p etc. You set it!        mainComposition.renderSize = firstAsset.tracks(withMediaType: AVMediaTypeVideo)[0].naturalSize        //We create an export session (you can't use PresetPassthrough because we are manipulating the transforms of the videos and the quality, so I just set it to highest)        guard let exporter = AVAssetExportSession(asset: mixComposition, presetName: AVAssetExportPresetHighestQuality) else { return }        //Provide type of file, provide the url location you want exported to (I don't have mine posted in this example).        exporter.outputFileType = AVFileTypeMPEG4        exporter.outputURL = url        //Then we tell the exporter to export the video according to our video framework, and it does the work!        exporter.videoComposition = mainComposition        //Asynchronous methods FTW!        exporter.exportAsynchronously(completionHandler: { //Do whatever when it finishes!        })    }}

这里有很多事情要做,但无论如何,还是必须要做的!抱歉,发布时间花了很长时间,如果您有任何疑问,请告诉我。



转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/484535.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号