Используя активы

Активы могут прибыть из файла или из носителей в библиотеке iPod пользователя или фото библиотеке. То, когда Вы создаете объект актива вся информация, которую Вы могли бы хотеть получить для того элемента, не сразу доступно. Как только у Вас есть актив фильма, можно извлечь неподвижные изображения из него, транскодировать его к другому формату или обрезать содержание.

Создание объекта актива

Для создания актива для представления любого ресурса, что можно идентифицировать использование URL Вы используете AVURLAsset. Самый простой случай создает актив из файла:

NSURL *url = <#A URL that identifies an audiovisual asset such as a movie file#>;
AVURLAsset *anAsset = [[AVURLAsset alloc] initWithURL:url options:nil];

Опции для инициализации актива

AVURLAsset методы инициализации берут в качестве их второго параметра словарь опций. Единственный ключ, используемый в словаре, AVURLAssetPreferPreciseDurationAndTimingKey. Соответствующее значение является булевской переменной (содержавшийся в NSValue объект), который указывает, должен ли актив быть подготовлен указать точную продолжительность и обеспечить точный произвольный доступ ко времени.

Получение точной продолжительности актива может потребовать значительной обработки наверху. Используя приблизительную продолжительность обычно более дешевая работа и достаточный для воспроизведения. Таким образом:

  • Если Вы только намереваетесь играть актив, любую передачу nil вместо словаря или передачи словарь, содержащий AVURLAssetPreferPreciseDurationAndTimingKey ключ и соответствующее значение NO (содержавшийся в NSValue объект).

  • Если Вы хотите добавить актив к составу (AVMutableComposition), Вам обычно нужен точный произвольный доступ. Передайте словарь, содержащий AVURLAssetPreferPreciseDurationAndTimingKey ключ и соответствующее значение YES (содержавшийся в NSValue объект — вспоминает это NSNumber наследовался от NSValue):

    NSURL *url = <#A URL that identifies an audiovisual asset such as a movie file#>;
    NSDictionary *options = @{ AVURLAssetPreferPreciseDurationAndTimingKey : @YES };
    AVURLAsset *anAssetToUseInAComposition = [[AVURLAsset alloc] initWithURL:url options:options];

Доступ к активам пользователя

Для доступа к активам, которыми управляет библиотека iPod или фото приложением, необходимо получить URL актива, который Вы хотите.

  • Для доступа к Библиотеке iPod Вы создаете MPMediaQuery экземпляр для нахождения элемента, который Вы хотите затем получите его использование URL MPMediaItemPropertyAssetURL.

    Для больше о Библиотеке Носителей, см. Мультимедийное Руководство по программированию.

  • Для доступа к активам, которыми управляет фото приложение, Вы используете ALAssetsLibrary.

Следующий пример показывает, как можно заставить актив представлять первое видео в Сохраненном фото Альбоме.

ALAssetsLibrary *library = [[ALAssetsLibrary alloc] init];
 
// Enumerate just the photos and videos group by using ALAssetsGroupSavedPhotos.
[library enumerateGroupsWithTypes:ALAssetsGroupSavedPhotos usingBlock:^(ALAssetsGroup *group, BOOL *stop) {
 
// Within the group enumeration block, filter to enumerate just videos.
[group setAssetsFilter:[ALAssetsFilter allVideos]];
 
// For this example, we're only interested in the first item.
[group enumerateAssetsAtIndexes:[NSIndexSet indexSetWithIndex:0]
                        options:0
                     usingBlock:^(ALAsset *alAsset, NSUInteger index, BOOL *innerStop) {
 
                         // The end of the enumeration is signaled by asset == nil.
                         if (alAsset) {
                             ALAssetRepresentation *representation = [alAsset defaultRepresentation];
                             NSURL *url = [representation url];
                             AVAsset *avAsset = [AVURLAsset URLAssetWithURL:url options:nil];
                             // Do something interesting with the AV asset.
                         }
                     }];
                 }
                 failureBlock: ^(NSError *error) {
                     // Typically you should handle an error more gracefully than this.
                     NSLog(@"No groups");
                 }];
 

Подготовка актива для использования

Инициализация актива (или дорожка) не обязательно означает, что вся информация, которую Вы могли бы хотеть получить для того элемента, сразу доступна. Это может потребовать, чтобы некоторое время вычислило даже продолжительность элемента (файл MP3, например, может не содержать сводную информацию). Вместо того, чтобы блокировать текущий поток, в то время как значение вычисляется, необходимо использовать AVAsynchronousKeyValueLoading протокол чтобы попросить значения и вернуть ответ позже через обработчик завершения, Вы определяете использование блока. (AVAsset и AVAssetTrack соответствуйте AVAsynchronousKeyValueLoading протокол.)

Вы тестируете, загружается ли значение для использования свойства statusOfValueForKey:error:. Когда актив сначала загружается, значение большинства или все его свойства AVKeyValueStatusUnknown. Для загрузки значения для одного или более свойств Вы вызываете loadValuesAsynchronouslyForKeys:completionHandler:. В обработчике завершения Вы принимаете любые меры, является надлежащим в зависимости от состояния свойства. Вы должны всегда подготавливаться к загрузке не завершиться успешно, или потому что она перестала работать по некоторым причинам, такие как основанный на сети URL, являющийся недоступным, или потому что была отменена загрузка..

NSURL *url = <#A URL that identifies an audiovisual asset such as a movie file#>;
AVURLAsset *anAsset = [[AVURLAsset alloc] initWithURL:url options:nil];
NSArray *keys = @[@"duration"];
 
[asset loadValuesAsynchronouslyForKeys:keys completionHandler:^() {
 
    NSError *error = nil;
    AVKeyValueStatus tracksStatus = [asset statusOfValueForKey:@"duration" error:&error];
    switch (tracksStatus) {
        case AVKeyValueStatusLoaded:
            [self updateUserInterfaceForDuration];
            break;
        case AVKeyValueStatusFailed:
            [self reportError:error forAsset:asset];
            break;
        case AVKeyValueStatusCancelled:
            // Do whatever is appropriate for cancelation.
            break;
   }
}];

Если Вы хотите подготовить актив к воспроизведению, необходимо загрузить tracks свойство. Для больше об игре активов, посмотрите Воспроизведение.

Получение неподвижных изображений от видео

Для получения неподвижных изображений, таких как миниатюры от актива для воспроизведения Вы используете AVAssetImageGenerator объект. Вы инициализируете генератор изображения со своим активом. Инициализация может успешно выполниться, тем не менее, даже если актив не обладает никакими визуальными дорожками во время инициализации, поэтому если необходимый необходимо протестировать, имеет ли актив какие-либо дорожки с визуальным характерным использованием tracksWithMediaCharacteristic:.

AVAsset anAsset = <#Get an asset#>;
if ([[anAsset tracksWithMediaType:AVMediaTypeVideo] count] > 0) {
    AVAssetImageGenerator *imageGenerator =
        [AVAssetImageGenerator assetImageGeneratorWithAsset:anAsset];
    // Implementation continues...
}

Можно сконфигурировать несколько аспектов генератора изображения, например, можно указать максимальные размерности для изображений, которые он генерирует и апертурное использование режима maximumSize и apertureMode соответственно. Можно тогда генерировать единственное изображение в установленный срок или серию изображений. Необходимо гарантировать, чтобы Вы сохранили сильную ссылку к генератору изображения, пока он не генерировал все изображения.

Генерация единственного изображения

Вы используете copyCGImageAtTime:actualTime:error: генерировать единственное изображение в определенное время. Основа AV может не быть в состоянии произвести изображение в точно время, которое Вы запрашиваете, таким образом, можно передать как второй параметр указатель на CMTime, по возврату содержащий время, в которое было фактически сгенерировано изображение.

AVAsset *myAsset = <#An asset#>];
AVAssetImageGenerator *imageGenerator = [[AVAssetImageGenerator alloc] initWithAsset:myAsset];
 
Float64 durationSeconds = CMTimeGetSeconds([myAsset duration]);
CMTime midpoint = CMTimeMakeWithSeconds(durationSeconds/2.0, 600);
NSError *error;
CMTime actualTime;
 
CGImageRef halfWayImage = [imageGenerator copyCGImageAtTime:midpoint actualTime:&actualTime error:&error];
 
if (halfWayImage != NULL) {
 
    NSString *actualTimeString = (NSString *)CMTimeCopyDescription(NULL, actualTime);
    NSString *requestedTimeString = (NSString *)CMTimeCopyDescription(NULL, midpoint);
    NSLog(@"Got halfWayImage: Asked for %@, got %@", requestedTimeString, actualTimeString);
 
    // Do something interesting with the image.
    CGImageRelease(halfWayImage);
}

Генерация последовательности изображений

Для генерации серии изображений Вы отправляете генератор изображения a generateCGImagesAsynchronouslyForTimes:completionHandler: сообщение. Первым параметром является массив NSValue объекты, каждый содержащий a CMTime структура, указывая времена актива, в течение которых Вы хотите, чтобы были сгенерированы изображения. Вторым параметром является блок, служащий обратным вызовом, вызванным для каждого сгенерированного изображения. Блочные параметры обеспечивают результат, постоянный, который говорит Вам, создавалось ли изображение успешно или если работа была отменена, и, как надлежащая:

  • Изображение

  • Время, на которое Вы запросили изображение и фактическое время, в течение которого было сгенерировано изображение

  • Ошибочный объект, описывающий причину отказавшая генерация

В Вашей реализации блока проверьте результат, постоянный, чтобы определить, создавалось ли изображение. Кроме того, гарантируйте, чтобы Вы сохранили сильную ссылку к генератору изображения, пока он не закончил создавать изображения.

AVAsset *myAsset = <#An asset#>];
// Assume: @property (strong) AVAssetImageGenerator *imageGenerator;
self.imageGenerator = [AVAssetImageGenerator assetImageGeneratorWithAsset:myAsset];
 
Float64 durationSeconds = CMTimeGetSeconds([myAsset duration]);
CMTime firstThird = CMTimeMakeWithSeconds(durationSeconds/3.0, 600);
CMTime secondThird = CMTimeMakeWithSeconds(durationSeconds*2.0/3.0, 600);
CMTime end = CMTimeMakeWithSeconds(durationSeconds, 600);
NSArray *times = @[NSValue valueWithCMTime:kCMTimeZero],
                  [NSValue valueWithCMTime:firstThird], [NSValue valueWithCMTime:secondThird],
                  [NSValue valueWithCMTime:end]];
 
[imageGenerator generateCGImagesAsynchronouslyForTimes:times
                completionHandler:^(CMTime requestedTime, CGImageRef image, CMTime actualTime,
                                    AVAssetImageGeneratorResult result, NSError *error) {
 
                NSString *requestedTimeString = (NSString *)
                    CFBridgingRelease(CMTimeCopyDescription(NULL, requestedTime));
                NSString *actualTimeString = (NSString *)
                    CFBridgingRelease(CMTimeCopyDescription(NULL, actualTime));
                NSLog(@"Requested: %@; actual %@", requestedTimeString, actualTimeString);
 
                if (result == AVAssetImageGeneratorSucceeded) {
                    // Do something interesting with the image.
                }
 
                if (result == AVAssetImageGeneratorFailed) {
                    NSLog(@"Failed with error: %@", [error localizedDescription]);
                }
                if (result == AVAssetImageGeneratorCancelled) {
                    NSLog(@"Canceled");
                }
  }];

Можно отменить генерацию последовательности изображений путем отправки генератора изображения a cancelAllCGImageGeneration сообщение.

Обрезка и транскодирование фильма

Можно транскодировать фильм от одного формата до другого и обрезать фильм, с помощью AVAssetExportSession объект. Сеанс экспорта является объектом контроллера, управляющим асинхронным экспортом актива. Вы инициализируете сеанс с помощью актива, который Вы хотите экспортировать и имя предварительной установки экспорта, указывающей опции экспорта, которые Вы хотите применить (см. allExportPresets). Вы тогда конфигурируете сеанс экспорта для указания вывода URL и типа файла, и дополнительно других настроек, таких как метаданные и должен ли вывод быть оптимизирован для сетевого использования.

../Art/export_2x.png

Можно проверить, можно ли экспортировать данный актив с помощью данного предварительно установленного использования exportPresetsCompatibleWithAsset: как проиллюстрировано в этом примере:

AVAsset *anAsset = <#Get an asset#>;
NSArray *compatiblePresets = [AVAssetExportSession exportPresetsCompatibleWithAsset:anAsset];
if ([compatiblePresets containsObject:AVAssetExportPresetLowQuality]) {
    AVAssetExportSession *exportSession = [[AVAssetExportSession alloc]
        initWithAsset:anAsset presetName:AVAssetExportPresetLowQuality];
    // Implementation continues.
}

Вы завершаете конфигурацию сеанса путем обеспечения вывода URL (URL должен быть файлом URL.) AVAssetExportSession может вывести тип выходного файла из расширения пути URL’s; обычно, однако, Вы устанавливаете его непосредственно использование outputFileType. Можно также указать дополнительные свойства, такие как диапазон времени, предел для длины выходного файла, должен ли экспортируемый файл быть оптимизирован для сетевого использования и видео состава. Следующий пример иллюстрирует, как использовать timeRange свойство для обрезки фильма:

    exportSession.outputURL = <#A file URL#>;
    exportSession.outputFileType = AVFileTypeQuickTimeMovie;
 
    CMTime start = CMTimeMakeWithSeconds(1.0, 600);
    CMTime duration = CMTimeMakeWithSeconds(3.0, 600);
    CMTimeRange range = CMTimeRangeMake(start, duration);
    exportSession.timeRange = range;

Для создания нового файла Вы вызываете exportAsynchronouslyWithCompletionHandler:. Когда работа экспорта заканчивается, блок обработчика завершения вызывают; в Вашей реализации обработчика необходимо проверить сеанс status значение, чтобы определить, был ли экспорт успешным, отказавшим, или было отменено:

    [exportSession exportAsynchronouslyWithCompletionHandler:^{
 
        switch ([exportSession status]) {
            case AVAssetExportSessionStatusFailed:
                NSLog(@"Export failed: %@", [[exportSession error] localizedDescription]);
                break;
            case AVAssetExportSessionStatusCancelled:
                NSLog(@"Export canceled");
                break;
            default:
                break;
        }
    }];

Можно отменить экспорт путем отправки сеанса a cancelExport сообщение.

При попытке перезаписать существующий файл или записать файл за пределами песочницы приложения, экспорт перестанет работать. Это может также перестать работать если:

В этих ситуациях необходимо обычно сообщать пользователю что неработающий экспорт, затем позволить пользователю перезапускать экспорт.