[iOS] AVPlayerItemVideoOutput.hasNewPixelBufferForItemTime работает некорректно

Это мой первый вопрос здесь, так что не будьте серьезными.

Я воспроизвожу видео из сети с помощью AVPlayer. Я вывожу текущий кадр, используя AVPlayerItemVideoOutput, прикрепленный к AVPlayerItem, который воспроизводится AVPlayer. Чтобы проверить, готов ли новый кадр, я вызываю [AVPlayerItemVideoOutput hasNewPixelBufferForItemTime], затем выводит его с помощью OpenGL ES. Все работает отлично, если я читаю mp4, но если я пытаюсь читать m3u8, он работает около 1 секунды (~ 30 кадров), но после этого периода [AVPlayerItemVideoOutput hasNewPixelBufferForItemTime] начинает возвращать только FALSE, поэтому текущий кадр не обновляется.

В случае, если я ищу текущий кадр, используя [AVPlayer seekToTime], прежде чем эта проблема начнется, все будет нормально.

Тест m3u8 video Я использую здесь жизни:

http://195.16.112.71/adaptive/3006a26a-9154-4b38-a327-4fa2a2381ae6.video/3006a26a-9154-4b38-a327-4fa2a2381ae6.m3u8

Чтобы воспроизвести эту проблему, я модифицировал образец Apple AVPlayerDemo, вот он: https://yadi.sk/d/T2aVGoKnWmf5Z

Основное изменение заключается в том, что я вызываю [AVPlayerDemoPlaybackViewController update], который вызывает упомянутый [AVPlayerItemVideoOutput hasNewPixelBufferForItemTime]. Эта функция имеет статическую переменную счетчик, которая хранит количество успешных вызовов [AVPlayerItemVideoOutput copyPixelBufferForItemTime].

URL-адрес видеоролика установлен в [AVPlayerDemoPlaybackViewController setURL], он жестко запрограммирован в начале функции. По умолчанию его значение указывает на видео m3u8, которое воспроизводит проблему, в этом случае среднее значение счетчика составляет около 30, после кадра с этим индексом [AVPlayerItemVideoOutput имеетNewPixelBufferForItemTime] возвращает только FALSE.

В случае, когда используется другой URL-адрес видео (см. начало [AVPlayerDemoPlaybackViewController setURL] - есть альтернативный Url, который вы можете раскомментировать), все кадры успешно прочитаны.

Любая помощь будет оценена!

Ответ 1

Удар кода не решает мою проблему, я все еще ничего не получил от [AVPlayerItemVideoOutput hasNewPixelBufferForItemTime]

if (failedCount > 100) {
    failedCount = 0;
    [_playerItem removeOutput:_output];
    [_playerItem addOutput:_output];
}

Наконец, после тестирования моего кода на целый день. Я нашел способ решить эту проблему.

#pragma mark - AVPlayerItemOutputPullDelegate
- (void)outputMediaDataWillChange:(AVPlayerItemOutput *)sender {
    if (![self.videoOutput hasNewPixelBufferForItemTime:CMTimeMake(1, 10)]) {
        [self configVideoOutput];
    }
    [self.displayLink setPaused:NO];
}

Проверьте [AVPlayerItemVideoOutput hasNewPixelBufferForItemTime] при вызове outputMediaDataWillChange:. Восстановите свой AVPlayerItemVideoOutput, если новый буфер пикселей не равен 0,1 с.

Код в [self configVideoOutput]; просто заново создайте новый AVPlayerItemVideoOutput, чтобы заменить текущее свойство videoOutput.

Почему 0.1s?

Я тестировал и экспериментировал много раз, я обнаружил, что первый 1 или 2 кадр всегда может не иметь пиксельного буфера. Итак, первые 1/30s, 2/30s (для видео со скоростью 30 кадров в секунду) могут не иметь кадрового и пиксельного буфера. Но если нет пикселя видео пикселя после 0,1 с, выход видео может сломаться или что-то проблема с ним. Поэтому нам нужно воссоздать его.

Ответ 2

Я заметил, что AVPlayerItemVideoOutput "застревает" как-то при использовании плейлистов HLS с несколькими титрами. Когда игрок переходит на более высокий битрейт → трек из проигрывателя, видеозапись изменена → он получит несколько пиксельных буферов, но после этого hasNewPixelBufferForItemTime всегда будет возвращать NO.

Я провел дни с этой проблемой. Случайно я заметил, что если я пойду на задний план и после этого вернусь на передний план → Видео будет воспроизводиться нормально с более высоким битрейтом. Это не решение.

Наконец, я нашел решение этой проблемы. Я устанавливаю счетчик неудачных пиксельных буферов, после 100 ошибок я удаляю текущий вывод из playeritem и устанавливаю тот же экземпляр обратно.

if (failedCount > 100)
    {
        failedCount = 0;
        [_playerItem removeOutput:_output];
        [_playerItem addOutput:_output];
    }

Ответ 3

У меня получилось очень приятное решение: убедитесь, что вы вызываете - (void)addOutput:(AVPlayerItemOutput *)output метод AVPlayerItem только ПОСЛЕ того, как вы заметили, что status вашего AVPlayerItem - AVPlayerItemStatusReadyToPlay.

Ссылка: Посмотрите ответ depinxi на этой странице