1

I'm using GPUImage in my application and trying to filter video. Live video filtering is working well. The trouble comes up when I try to read a video into memory from the filesystem and apply filters using the code posted on the sunsetlakessoftware tutorial page and in the SimpleVideoFileFilter demo.

EDIT: I realized that my original post may not have been posing a specific enough question. What I am asking is: How exactly can I read a video from disk into memory, apply a GPUImageFilter, and then overwrite the original with the filtered version?

The application is crashing with the following error:

-[AVAssetWriter startWriting] Cannot call method when status is 2

status 2 is AVAssetWriterStatusCompleted. I've seen the same failure occur with all three other AVAssetWriterStatuses.

I have posted the relevant code below.

GPUImageFilter *selectedFilter = [self.allFilters objectAtIndex:indexPath.item];

// get the file url I stored when the video was initially captured
NSURL *url = [self.videoURLsByIndexPath objectForKey:self.indexPathForDisplayedImage];

GPUImageMovie *movieFile = [[GPUImageMovie alloc] initWithURL:url];
movieFile.runBenchmark = YES;
movieFile.playAtActualSpeed = NO;
[movieFile addTarget:selectedFilter]; // apply the user-selected filter to the file

unlink([url.absoluteString UTF8String]); // delete the file that was at that file URL so it's writeable

// A different movie writer than the one I was using for live video capture.
GPUImageMovieWriter *editingMovieWriter = [[GPUImageMovieWriter alloc] initWithMovieURL:url size:CGSizeMake(640.0, 640.0)];

[selectedFilter addTarget:editingMovieWriter];

editingMovieWriter.shouldPassthroughAudio = YES;
movieFile.audioEncodingTarget = editingMovieWriter;
[movieFile enableSynchronizedEncodingUsingMovieWriter:editingMovieWriter];

[editingMovieWriter startRecording];
[movieFile startProcessing]; // Commenting out this line prevents crash

// weak variables to prevent retain cycle
__weak GPUImageMovieWriter *weakWriter = editingMovieWriter;
__weak id weakSelf = self;
[editingMovieWriter setCompletionBlock:^{
    [selectedFilter removeTarget:weakWriter];
    [weakWriter finishRecording];
    [weakSelf savePhotosToLibrary]; // use ALAssetsLibrary to write to camera roll
}];

Perhaps my issue is with the scope of the editingMovieWriter. Or perhaps the fact that I am initializing a GPUImageMovie instance with the same URL that I am attempting to write to. I have read several posts on the issues page of the GPUImage github, several related posts on SO, the readme, and the tutorial linked above.

Any insight into this issue would be greatly appreciated. Thanks.

geraldWilliam
  • 4,123
  • 1
  • 23
  • 35

2 Answers2

1

There's at least one thing here that might be behind this. In the code above, you're not hanging on to a strong reference to your movieFile source object.

If this is an ARC-enabled project, that object will be deallocated the instant that you complete your setup method (if it's not, you'll be leaking that object). That will stop movie playback, deallocate the movie itself, and lead to black frames being sent down the filter pipeline (other potential instabilities).

You need to make movieFile a strongly-referenced instance variable to make sure it hangs on past this setup method, since all movie processing is asynchronous.

Brad Larson
  • 170,088
  • 45
  • 397
  • 571
  • Thanks for your reply. Good point. I've added a strong property to hang on to my movie file. Unfortunately, it did not resolve the issue. I'm finding that when `[movieFile startProcessing]` is called, the status of `editingMovieWriter.assetWriter` changes to `AVAssetWriterStatusFailed`. I've confirmed that the GPUImageMovieWriter is a different instance (which, of course, means that the AVAssetWriter is a new one.) I can't seem to pin down the cause of the writer's failure. If you have a moment and any further thoughts, that would be awesome. – geraldWilliam Mar 25 '14 at 21:33
  • 1
    @geraldWilliam - Can you modify the SimpleVideoFileFilter example (which should work cleanly) to the point at which it breaks? That could help identify what's going wrong here. – Brad Larson Mar 25 '14 at 21:35
  • Well, I got that same error when I initialized the movieWriter with what I knew to be an invalid file URL. That was a big hint. Went back in to my project and generated a new file URL (not the same one I read from) to write the modified video to. No more crash. Is the lib actively reading from that URL? And therefore it's not a valid write location? I guess I assumed that initializing GPUImageMovie with a URL loaded the whole file in. Whatever the case, thanks for your help. – geraldWilliam Mar 25 '14 at 22:09
  • @geraldWilliam - To be honest, the movie reading and writing functionality is probably the roughest area of the framework, since it's not something I use on a regular basis. There might well be something wrong in the way resources are being handled in there. – Brad Larson Mar 25 '14 at 22:11
0
Here is solution : 

Declare it 
    var movieFile: GPUImageMovie!
    var gpuImage: GPUImagePicture!
    var sourcePicture: GPUImagePicture!
    var sepiaFilter: GPUImageOutput!
    var sepiaFilter2: GPUImageInput! 
    var  movieWriter : GPUImageMovieWriter!
    var filter: GPUImageInput!

//Filter image 

  func StartWriting()
    {

        // Step - 1  pass url to  avasset
        let loadingNotification = MBProgressHUD.showHUDAddedTo(self.view, animated: true)
        loadingNotification.mode = MBProgressHUDMode.Indeterminate
        loadingNotification.labelText = "Loading"

        let documentsURL1 = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)[0] as! NSURL
        let pathToMovie = documentsURL1.URLByAppendingPathComponent("temp.mov")
        self.movieFile = GPUImageMovie(URL: pathToMovie)
        self.movieFile.runBenchmark = true
        self.movieFile.playAtActualSpeed = false
        self.filter = GPUImageGrayscaleFilter()
        self.sepiaFilter = GPUImageGrayscaleFilter()
        self.movieFile.addTarget(self.filter)
        let documentsURL2 = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)[0] as! NSURL
        self.paths = documentsURL2.URLByAppendingPathComponent("temp1.mov")
        var fileManager: NSFileManager = NSFileManager.defaultManager()
        var error: NSError
        fileManager.removeItemAtURL(self.paths, error: nil)
        let Data = NSData(contentsOfURL: pathToMovie)
        println( Data?.length )

        var anAsset = AVAsset.assetWithURL(pathToMovie)as!AVAsset

        var videoAssetTrack = anAsset.tracksWithMediaType(AVMediaTypeVideo)[0]as! AVAssetTrack
        var videoAssetOrientation_: UIImageOrientation = .Up
        var isVideoAssetPortrait_: Bool = true
        var videoTransform: CGAffineTransform = videoAssetTrack.preferredTransform

        var naturalSize = CGSize()
        var FirstAssetScaleToFitRatio: CGFloat = 320.0 / videoAssetTrack.naturalSize.width
        println(naturalSize)
        naturalSize = videoAssetTrack.naturalSize
        self.movieWriter = GPUImageMovieWriter(movieURL: self.paths, size: naturalSize)
        let input = self.filter as! GPUImageOutput
        input.addTarget(self.movieWriter)
        self.movieWriter.shouldPassthroughAudio = true
        if anAsset.tracksWithMediaType(AVMediaTypeAudio).count > 0 {
            self.movieFile.audioEncodingTarget =  self.movieWriter
        }
        else
        {
            self.movieFile.audioEncodingTarget = nil
        }

        self.movieFile.enableSynchronizedEncodingUsingMovieWriter(self.movieWriter)
        self.movieWriter.startRecording()
        self.movieFile.startProcessing()

        self.movieWriter.completionBlock =

            {() -> Void in

                self.movieWriter.finishRecording()


                self.obj.performWithAsset(self.paths)



        }
        let delayTime1 = dispatch_time(DISPATCH_TIME_NOW, Int64(15 * Double(NSEC_PER_SEC)))
        dispatch_after(delayTime1, dispatch_get_main_queue()) {
            MBProgressHUD.hideAllHUDsForView(self.view, animated: true)


        }
        hasoutput = true ;
    }
shikha kochar
  • 163
  • 1
  • 7