[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 на этой странице