iPhone Чтение UIimage (кадров) из видео с помощью AVFoundation

Извините за мой английский) В поисках информации о чтении кадров из видео с iPhone я нашел этот проект, http://www.codza.com/extracting-frames-from-movies-on-iphone/comment-page-1#comment-1116 , но я также где-то читал, что вы можете использовать AVFoundation для захвата кадров из видео для повышения производительности.

Но я не могу найти информацию о том, как я могу это сделать...

Какая-то идея?

Спасибо за чтение


person matiasfha    schedule 16.11.2010    source источник


Ответы (5)


Вы говорите об использовании вызовов для создания того, что Apple называет миниатюрами из видео в определенное время.

Для MPMoviePlayerController (то, что iOS использует для хранения видео из файла или другого источника) для этого есть две команды. Первый генерирует одну миниатюру (изображение) из фильма в определенный момент времени, а второй генерирует набор миниатюр для временного диапазона.

В этом примере показано, как получить изображение на 10-й секунде видеоклипа myMovie.mp4:

MPMoviePlayerController *movie = [[MPMoviePlayerController alloc]
        initWithContentURL:[NSURL URLWithString:@"myMovie.mp4"]];
UIImage *singleFrameImage = [movie thumbnailImageAtTime:10 
        timeOption:MPMovieTimeOptionExact];

Обратите внимание, что это выполняется синхронно, т. е. пользователь будет вынужден ждать, пока вы получите снимок экрана.

Другой вариант — получить серию изображений из фильма из массива раз:

MPMoviePlayerController *movie = [[MPMoviePlayerController alloc]
        initWithContentURL [NSURL URLWithString:@"myMovie.mp4"]];
NSNumber time1 = 10;
NSNumber time2 = 11;
NSNumber time3 = 12;
NSArray *times = [NSArray arrayWithObjects:time1,time2,time3,nil];
[movie requestThumbnailImagesAtTimes:times timeOption:MPMovieTimeOptionExact];

Этот второй способ будет запускать уведомление типа MPMoviePlayerThumbnailImageRequestDidFinishNotification каждый раз, когда создается новое изображение. Вы можете настроить наблюдателя для наблюдения за этим и обработки изображения — я оставлю вас, чтобы вы сами поработали над этим!

person h4xxr    schedule 16.11.2010
comment
Спасибо!! .. это хорошая идея .. я решаю это с помощью ffmpeg, но мне не нужно что-то родное для iOS SDK .. я попробую вашу идею - person matiasfha; 18.11.2010
comment
о первой части, у вас есть идеи, как это сделать асинхронно? - person Vincent Bacalso; 10.09.2011
comment
@BacalsoVincent, вы бы не стали делать первый способ, если вам это нужно асинхронно ... если вам нужно только одно изображение, но асинхронно, используйте второй метод с массивом только один раз :) - person h4xxr; 13.11.2011
comment
Проблема с этим методом заключается в том, что он дает сбой, если вы запускаете приложение в фоновом режиме. - person Dex; 20.04.2012
comment
MPMoviePlayerThumbnailImageRequestDidFinishУведомление не работает - person KETAN; 05.05.2012
comment
Является ли этот метод извлечения правдоподобным, я имею в виду, действительно ли он хорошо работает? На самом деле я могу реализовать ту же функциональность, используя iFrameExtractor github.com/lajos/iFrameExtractor. Но я не хочу идти с помощью этого метода и думаю о реализации этого метода... Поэтому, пожалуйста, дайте мне правильное направление !! заранее спасибо - person Parvez Belim; 02.07.2013
comment
Я также сталкиваюсь с той же проблемой, что MPMoviePlayerThumbnailImageRequestDidFinishNotification не запускается - person Heena; 28.02.2014

Вы также можете попробовать AVAssetImageGenerator, в частности generateCGImagesAsynchronouslyForTimes:completionHandler.

Этот ответ SO содержит хороший пример кода.

person Steve    schedule 18.11.2010

Код Swift 2 для получения кадров с помощью AVAssetImageGenerator:

func previewImageForLocalVideo(url:NSURL) -> UIImage?
{
    let asset = AVAsset(URL: url)
    let imageGenerator = AVAssetImageGenerator(asset: asset)
    imageGenerator.appliesPreferredTrackTransform = true

    var time = asset.duration
    //If possible - take not the first frame (it could be completely black or white on camara's videos)
    time.value = min(time.value, 2)

    do {
        let imageRef = try imageGenerator.copyCGImageAtTime(time, actualTime: nil)
        return UIImage(CGImage: imageRef)
    }
    catch let error as NSError
    {
        print("Image generation failed with error \(error)")
        return nil
    }
}
person Avt    schedule 27.10.2015

Вот код для получения изображения FPS из видео

1) Импорт

#import <Photos/Photos.h>

2) в представленииDidLoad

    videoUrl = [NSURL fileURLWithPath:[[NSBundle mainBundle]pathForResource:@"VfE_html5" ofType:@"mp4"]];
    [self createImage:5]; // 5 is frame per second (FPS) you can change FPS as per your requirement.

3) Функции

-(void)createImage:(int)withFPS {
    AVURLAsset *asset = [[AVURLAsset alloc] initWithURL:videoUrl options:nil];
    AVAssetImageGenerator *generator = [[AVAssetImageGenerator alloc] initWithAsset:asset];
    generator.requestedTimeToleranceAfter =  kCMTimeZero;
    generator.requestedTimeToleranceBefore =  kCMTimeZero;

    for (Float64 i = 0; i < CMTimeGetSeconds(asset.duration) *  withFPS ; i++){
        @autoreleasepool {
            CMTime time = CMTimeMake(i, withFPS);
            NSError *err;
            CMTime actualTime;
            CGImageRef image = [generator copyCGImageAtTime:time actualTime:&actualTime error:&err];
            UIImage *generatedImage = [[UIImage alloc] initWithCGImage:image];
            [self savePhotoToAlbum: generatedImage]; // Saves the image on document directory and not memory
            CGImageRelease(image);
        }
    }
}

-(void)savePhotoToAlbum:(UIImage*)imageToSave {

    [[PHPhotoLibrary sharedPhotoLibrary] performChanges:^{
        PHAssetChangeRequest *changeRequest = [PHAssetChangeRequest creationRequestForAssetFromImage:imageToSave];
    } completionHandler:^(BOOL success, NSError *error) {
        if (success) {
            NSLog(@"sucess.");
        }
        else {
            NSLog(@"fail.");
        }
    }];
}
person Hardik Thakkar    schedule 17.07.2018
comment
строки generator.requestedTimeToleranceAfter = kCMTimeZero; generator.requestedTimeToleranceBefore = kCMTimeZero; кажутся очень важными. в противном случае генератор по умолчанию возвращает только определенные моменты времени из видео, независимо от того, какой момент времени вы запрашиваете. - person peter; 11.12.2019

В Swift 4 это сработало для меня с некоторыми изменениями, в основном изменением параметра "at" imageGenerator.copyCGImage на тип CMTime:

func showFrame(from file:String) {
    let file = file.components(separatedBy: ".")
    guard let path = Bundle.main.path(forResource: file[0], ofType:file[1]) else {
        debugPrint( "\(file.joined(separator: ".")) not found")
        return
    }
    let url = URL(fileURLWithPath: path)
    let image = previewImageForLocalVideo(url: url)
    let imgView = UIImageView(image: image)
    view.addSubview(imgView)
}    

func previewImageForLocalVideo(url:URL) -> UIImage? {
    let asset = AVAsset(url: url)
    let imageGenerator = AVAssetImageGenerator(asset: asset)
    imageGenerator.appliesPreferredTrackTransform = true
    let tVal = NSValue(time: CMTimeMake(12, 1)) as! CMTime
    do {
        let imageRef = try imageGenerator.copyCGImage(at: tVal, actualTime: nil)
        return UIImage(cgImage: imageRef)
    }
    catch let error as NSError
    {
        print("Image generation failed with error \(error)")
        return nil
    }
}

override func viewDidLoad() {
    super.viewDidLoad()
    showFrame(from:"video.mp4")
}

Источник

person Mike Lee    schedule 25.02.2018