Правильный способ быстрого переключения между видео - PullRequest
8 голосов
/ 08 марта 2012

Я создаю приложение, которое отображает определенное видео на основе внешнего события, для которого может потребоваться быстрое изменение воспроизводимого видео - один раз в секунду или более. Однако между видео не должно быть зазора или лага.

Каков наилучший способ сделать это? Есть только четыре видео, каждое около двух мегабайт.

Я подумывал о создании четырех MPMoviePlayerControllers, чтобы их представления были добавлены в основной вид, но скрыты, и переключались путем приостановки и скрытия текущего видео, а затем скрытия и воспроизведения следующего видео. Есть ли более элегантное решение?

Редактировать

Вот еще немного информации для моей точной ситуации:

  • Разные видеокадры имеют в основном общие пиксели, так что это нормально для кадра, который прикрепляется во время переключения, но НЕ в порядке для появления черных кадров.
  • Каждое видео длится всего около десяти секунд, и есть только четыре видео. Общие переходы состояния: 1 <-> 2 <-> 3 <-> 4-> 1.
  • Воспроизведение видео должно быть совместимо с одновременной записью AVAudioRecorder. Насколько я могу судить, MPMoviePlayerController - это не так.

Ответы [ 2 ]

8 голосов
/ 10 марта 2012

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

Однако это конкретное решение потенциально проблематично, поскольку у каждого проигрывателя фильмов есть собственный пользовательский интерфейс.Пользовательские интерфейсы не синхронизируются друг с другом, поэтому один может показывать панель управления, другой нет;кто-то может находиться в полноэкранном режиме и т. д. Переключение между ними может вызвать визуальные разрывы.

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

Я бы предложил перейти к нижележащему слою, AV Foundation.Теоретически вы можете просто создать AVPlayerItem для каждого видео.Это объект управления потоком без дополнительных затрат на пользовательский интерфейс, поэтому он идеально подходит для того, что вы делаете.Затем вы можете - опять-таки теоретически - создать один AVPlayer и один AVPlayerLayer для обработки дисплея.Если вы хотите переключиться с одного потока AVPlayerItem на другой, вы можете вызвать replaceCurrentItemWithPlayerItem: сообщение AVPlayer для обмена потоком данных.

Я сделал небольшой тестовый проект (GitHub) для проверкиэто, и, к сожалению, простое решение не совсем идеально.Отсутствие сбоев видеопотока, но при переходе от AVPlayer к AVPlayer уровень представления, кажется, кратко мигает последним просмотренным кадром предыдущего фильма в размере, соответствующем следующему фильму.Кажется, это помогает выделить отдельные объекты AVPlayer для каждого фильма и переключаться между ними на постоянный слой проигрывателя.Кажется, мгновенная вспышка фона все еще присутствует, но, по крайней мере, это тонкий дефект.Вот суть кода:

@interface ViewController : UIViewController
{
    NSMutableArray *players;
    AVPlayerLayer *playerLayer;
}

@property (nonatomic) IBOutlet UIView *videoView;

- (IBAction) selectVideo:(id)sender;

@end

@implementation ViewController

@synthesize videoView;

- (void)viewDidLoad
{
    [super viewDidLoad];
    NSArray *videoTitles = [NSArray arrayWithObjects:@"Ultimate Dog Tease", 
        @"Backin Up", @"Herman Cain", nil];
    players = [NSMutableArray array];
    for (NSString *title in videoTitles) {
        AVPlayerItem *player = [AVPlayer playerWithURL:[[NSBundle mainBundle] 
            URLForResource:title withExtension:@"mp4"]];
        [player addObserver:self forKeyPath:@"status" options:0 context:nil];
        [players addObject:player];
    }
    playerLayer = [AVPlayerLayer playerLayerWithPlayer:[players objectAtIndex:0]];
    playerLayer.frame = self.videoView.layer.bounds;
    playerLayer.videoGravity = AVLayerVideoGravityResizeAspect;
    [self.videoView.layer addSublayer:playerLayer];
}

- (void) observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object 
    change:(NSDictionary *)change context:(void *)context
{
    [object removeObserver:self forKeyPath:@"status"];
    for (AVPlayer *player in players) {
         if (player.status != AVPlayerStatusReadyToPlay) {
             return;
         }
    }
    // All videos are ready to go
    [self playItemAtIndex:0];
}

- (void) playItemAtIndex:(NSUInteger)idx
{
    AVPlayer *newPlayer = [players objectAtIndex:idx];
    if (newPlayer != playerLayer.player) {
        [playerLayer.player pause];
        playerLayer.player = newPlayer;
    }
    [newPlayer play];
}

- (IBAction) selectVideo:(id)sender 
{
    [self playItemAtIndex:((UILabel *)sender).tag];
}

@end

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

Выделение трех отдельных слоев AVPlayer (в дополнение к трем слоям AVPlayer) предотвращает любую вспышку.К сожалению, AVPlayer, подключенный к невысказанному AVPlayerLayer, похоже, предполагает, что ему не нужно поддерживать видеобуфер.Каждое переключение между слоями приводит к временному заиканию видео.Так что это бесполезно.

При использовании AV Foundation следует отметить несколько моментов:

1) Объект AVPlayer не имеет встроенной поддержки зацикленного воспроизведения.Вам придется наблюдать за концом текущего видео и вручную искать нулевое время.

2) Нет вообще никакого пользовательского интерфейса, кроме видеокадра, но, опять же, я предполагаю, что это может быть для вас преимуществом.

2 голосов
/ 13 марта 2012

MPMoviePlayerController является одноэлементным.Четыре экземпляра будут иметь один и тот же указатель, одно и то же представление и т. Д. С нативным проигрывателем, я думаю, у вас есть только две альтернативы: одна - изменить свойство contentURL, когда вы хотите выполнить переход.Если задержка таким образом неприемлема, другой альтернативой является создание более длинного видео с сцепленными более короткими клипами.Вы можете создавать очень быстрые переходы внутри одного более длинного клипа, устанавливая currentPlaybackTime.

...