8

Requirement :

It sounds like litte different but this is what I want to achieve. I want to make movie (.mov) file in reverse. Just like how we rewind the movie file. I also want maintain same frame rate as my video is containing.

NOTE: I do not just want to play video file in reverse order. I want to generate new movie file playing in reverse order.

My Exploration :

I thought of below steps to perform to do the same.

  1. Make chunks of video files with specific frame rate using AVAssetExportSession
  2. Merge all these video chunks into single movie file using AVMutableComposition and AVAssetExportSession.
  3. Also merge audio of each file into new video file during the merge process.

Using above steps , I am able to achieve resulting video file in reverse but I am having below concerns.

  1. It takes good amount of time if video is of long duration.
  2. It also consumes huge CPU cycles and memory to accomplish this process.

Does anybody is having any other optimized way to achieve this? Any suggestion will be appreciated.

BornCoder
  • 1,046
  • 8
  • 10
  • Is there any direct way like we can directly reverse video data buffer and it will reverse the whole video file? – BornCoder Aug 31 '12 at 07:10
  • 1
    How were you able to generate a new movie file in reverse order using AVAssetExportSession and AVMutableComposition? What is the setting in these classes that reverses the order? – GingerBreadMane Jan 30 '14 at 23:36
  • Have you find any answer for this? i am looking for same thing currently. My question is here http://stackoverflow.com/questions/27363888/avfoundation-reverse-video – Amrit Trivedi Dec 08 '14 at 19:40

3 Answers3

4

Here is my solution , maybe it can help you. https://github.com/KayWong/VideoReverse

KayWong
  • 51
  • 4
2

Swift 5, credit to Andy Hin as I based this on http://www.andyhin.com/post/5/reverse-video-avfoundation

    class func reverseVideo(inURL: URL, outURL: URL, queue: DispatchQueue, _ completionBlock: ((Bool)->Void)?) {
        let asset = AVAsset.init(url: inURL)
        guard
            let reader = try? AVAssetReader.init(asset: asset),
            let videoTrack = asset.tracks(withMediaType: .video).first
        else {
            assert(false)
            completionBlock?(false)
            return
        }

        let width = videoTrack.naturalSize.width
        let height = videoTrack.naturalSize.height

        let readerSettings: [String : Any] = [
            String(kCVPixelBufferPixelFormatTypeKey) : kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange,
        ]
        let readerOutput = AVAssetReaderTrackOutput.init(track: videoTrack, outputSettings: readerSettings)
        reader.add(readerOutput)
        reader.startReading()

        var buffers = [CMSampleBuffer]()
        while let nextBuffer = readerOutput.copyNextSampleBuffer() {
            buffers.append(nextBuffer)
        }
        let status = reader.status
        reader.cancelReading()
        guard status == .completed, let firstBuffer = buffers.first else {
            assert(false)
            completionBlock?(false)
            return
        }
        let sessionStartTime = CMSampleBufferGetPresentationTimeStamp(firstBuffer)

        let writerSettings: [String:Any] = [
            AVVideoCodecKey : AVVideoCodecType.h264,
            AVVideoWidthKey : width,
            AVVideoHeightKey: height,
        ]
        let writerInput: AVAssetWriterInput
        if let formatDescription = videoTrack.formatDescriptions.last {
            writerInput = AVAssetWriterInput.init(mediaType: .video, outputSettings: writerSettings, sourceFormatHint: (formatDescription as! CMFormatDescription))
        } else {
            writerInput = AVAssetWriterInput.init(mediaType: .video, outputSettings: writerSettings)
        }
        writerInput.transform = videoTrack.preferredTransform
        writerInput.expectsMediaDataInRealTime = false

        guard
            let writer = try? AVAssetWriter.init(url: outURL, fileType: .mp4),
            writer.canAdd(writerInput)
        else {
            assert(false)
            completionBlock?(false)
            return
        }

        let pixelBufferAdaptor = AVAssetWriterInputPixelBufferAdaptor.init(assetWriterInput: writerInput, sourcePixelBufferAttributes: nil)
        let group = DispatchGroup.init()

        group.enter()
        writer.add(writerInput)
        writer.startWriting()
        writer.startSession(atSourceTime: sessionStartTime)

        var currentSample = 0
        writerInput.requestMediaDataWhenReady(on: queue) {
            for i in currentSample..<buffers.count {
                currentSample = i
                if !writerInput.isReadyForMoreMediaData {
                    return
                }
                let presentationTime = CMSampleBufferGetPresentationTimeStamp(buffers[i])
                guard let imageBuffer = CMSampleBufferGetImageBuffer(buffers[buffers.count - i - 1]) else {
                    WLog("VideoWriter reverseVideo: warning, could not get imageBuffer from SampleBuffer...")
                    continue
                }
                if !pixelBufferAdaptor.append(imageBuffer, withPresentationTime: presentationTime) {
                    WLog("VideoWriter reverseVideo: warning, could not append imageBuffer...")
                }
            }

            // finish
            writerInput.markAsFinished()
            group.leave()
        }

        group.notify(queue: queue) {
            writer.finishWriting {
                if writer.status != .completed {
                    WLog("VideoWriter reverseVideo: error - \(String(describing: writer.error))")
                    completionBlock?(false)
                } else {
                    completionBlock?(true)
                }
            }
        }
    }
xaphod
  • 6,392
  • 2
  • 37
  • 45
  • With your code AVAssetReader status always fails, do you have any solution for this issue? – Coder ACJHP Apr 20 '22 at 13:20
  • This code is working in production / on the app store for me. Your problem is likely that the files you're trying to read cannot be handled by AVAssetReader. – xaphod Apr 21 '22 at 14:48
  • I think so, this issue may related with my project because I am trying to read same asset with 2 different AVAssetReader one of them read to render the seconds for reverse the asset. – Coder ACJHP Apr 22 '22 at 10:08
-3

you need to refer AVFoundation library to achieve your task..

i have done only 30 seconds video edit with AVAssetExportSession & AVMutableComposition.

this is the link which you need to refer and its very help full.

http://www.subfurther.com/blog/category/avfoundation/

And also if you wants to refer WWDC conference PDF for Editing Media it would be better.

this link total source : https://developer.apple.com/videos/wwdc/2010/ And this link covers Editing Media With AVFoundation

About memory cycles.. it also consumes more memory while Exporting.

Solid Soft
  • 1,872
  • 2
  • 25
  • 55