0

Hi I have been trying to figure out how to implement movie looping in GPUImage2, but have been unsuccessful so far. The MovieInput class in GPUImage2 uses AVAssetReader to playback the movie files, so I researched ways to loop AVAssetReader. I found this question on StackOverFlow dealing with this topic. AVFoundation to reproduce a video loop

The best answer was "AVAssetReader doesn't support seeking or restarting, it is essentially a sequential decoder. You have to create a new AVAssetReader object to read the same samples again."

I tried to figure out how to connect the old assetReader to a new one one and I was not very successful and it crashed every time.

I was recommended to try something like this, but I am not exactly sure how to write the function generateAssetReader.

public func start() {
    self.assetReader = generateAssetReader(asset: asset, readAudio: readAudio, videoOutputSettings: videoOutputSettings, audioOutputSettings: audioOutputSettings)

 asset.loadValuesAsynchronously(forKeys:["tracks"], completionHandler:{
            DispatchQueue.global(priority:DispatchQueue.GlobalQueuePriority.default).async(execute: {
                guard (self.asset.statusOfValue(forKey: "tracks", error:nil) == .loaded) else { return }

                guard self.assetReader.startReading() else {
                    print("Couldn't start reading")
                    return
                }

                var readerVideoTrackOutput:AVAssetReaderOutput? = nil;

                for output in self.assetReader.outputs {
                    if(output.mediaType == AVMediaTypeVideo) {
                        readerVideoTrackOutput = output;
                    }
                }

                while (self.assetReader.status == .reading) {
                    self.readNextVideoFrame(from:readerVideoTrackOutput!)
                }


    if assetReader.status == .completed {
        assetReader.cancelReading()
        self.assetReader = nil
        if self.loop {
            self.start()
        } else {
            self.endProcessing()
        }
    }
}

Would anyone have a clue into solving this looping problem? This is a link to entire code of the MovieInput class. https://github.com/BradLarson/GPUImage2/blob/master/framework/Source/iOS/MovieInput.swift

Community
  • 1
  • 1
  • what did the crash look like? – Rhythmic Fistman Feb 22 '17 at 03:26
  • Hi thank you for the response. I got a working loop. The problem is I am not sure how to make it infinite and the current way I am doing it has memory problem. I basically copied the first part to run the code and put it in the loop section. I also made a new asset. – Eric Lp Fisher Mar 12 '17 at 00:08
  • Looping it once around didn't seem to have any memory problems, however when I made a function to do the loop and had 20 Assetreaders. The loop didn't run as smoothly. How could I create new asset readers when I need them so I don't take up an unnecessary amount of memory and have this be an infinite process? Would I have to create a new class? – Eric Lp Fisher Mar 12 '17 at 00:11
  • 20 asset readers? that sounds like too many. something's not right there – Rhythmic Fistman Mar 12 '17 at 22:25
  • Lol I don't think it is either. Is there a way to cancel and delete asset readers so I can do an infinite loop? The problem is they seem to need to be initialized and I am not sure how to get around the initialization process? – Eric Lp Fisher Mar 12 '17 at 23:07
  • I think you should only need at most two asset readers at a time to loop continuously. It strikes me you could maybe do all this with an `AVPlayerLooper`, `AVPlayerItemOutput` and an `AVPlayer`. Three classes may not seem simpler at first sight, but you wouldn't have to deal with `AVAssetReader`s. – Rhythmic Fistman Mar 12 '17 at 23:14
  • You are probably right. It just is a lot of code to rewrite and I am not as familiar with how AVPlayer even though that is the better solution. How easy would it be to replace the AVAssetReader with AVPlayer, AVPlayerLooper and AVPlayerItem? Also looking at the GPUImage2 code for the MovieInput class I was worried that I might have to use AVAssetReader because I am not sure you accomplish that type of processing with AVPlayer. Would you know if that is possible? – Eric Lp Fisher Mar 12 '17 at 23:32
  • I don't know `GPUImage` well, what output do you need? Because `AVPlayerItemOutput` will give you a `CVPixelBufferRef` for a looping movie. – Rhythmic Fistman Mar 13 '17 at 00:33
  • I see that GPUImage2 does process the video with CVPixelBuffer. Would that still work even though it is not CVPixelBufferRef? – Eric Lp Fisher Mar 13 '17 at 00:55
  • Yeah, that's the swift version of the same thing – Rhythmic Fistman Mar 13 '17 at 01:26

1 Answers1

1

I found the answer in case anyone is wondering.

    public func createReader() -> AVAssetReader
{
    var assetRead:AVAssetReader!
    do{
        assetRead = try AVAssetReader.init(asset: self.asset)

        let outputSettings:[String:AnyObject] = [(kCVPixelBufferPixelFormatTypeKey as String):NSNumber(value:Int32(kCVPixelFormatType_420YpCbCr8BiPlanarFullRange))]
        let readerVideoTrackOutput = AVAssetReaderTrackOutput(track:self.asset.tracks(withMediaType: AVMediaTypeVideo)[0], outputSettings:outputSettings)
        readerVideoTrackOutput.alwaysCopiesSampleData = false

        assetRead.add(readerVideoTrackOutput)
    }catch{

    }

    return assetRead
}

public func start() {
    self.assetReader = createReader()
    asset.loadValuesAsynchronously(forKeys:["tracks"], completionHandler:{
        DispatchQueue.global(priority:DispatchQueue.GlobalQueuePriority.default).async(execute: {
            guard (self.asset.statusOfValue(forKey: "tracks", error:nil) == .loaded) else { return }

            guard self.assetReader.startReading() else {
                print("Couldn't start reading")
                return
            }

            var readerVideoTrackOutput:AVAssetReaderOutput? = nil;

            for output in self.assetReader.outputs {
                if(output.mediaType == AVMediaTypeVideo) {
                    readerVideoTrackOutput = output;
                }
            }

            while (self.assetReader.status == .reading) {
                self.readNextVideoFrame(from:readerVideoTrackOutput!)
            }

            if (self.assetReader.status == .completed) {
                self.assetReader.cancelReading()

                if (self.loop) {
                    // TODO: Restart movie processing
                    self.start()
                } else {
                    self.endProcessing()
                }
            }
        })
    })
}