android mediaplyer seekTo внутри onPrepared

моя проблема, кажется, происходит только на андроиде 4.2.2

Я иду таким образом состояние простоя -> инициализированное состояние -> prepareAsync () -> и в onPrepared вызывается seekTo, но в этой версии Android медиаплеер возвращает

"Попытка перейти к концу файла: request = {int>0}, durationMs = 0"

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

Что еще более странно, так это то, что если я вызову mediaPlayer.getDuration() в onPrepared(), он вернет правильное значение, а не 0.

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

это плавный потоковый видеоконтент


person Lukas Hanacek    schedule 09.04.2013    source источник


Ответы (3)


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

На 4.2 я заметил, что получены следующие обратные вызовы:

1) onVideoSizeChanged() - где высота и ширина = 0
2) onPrepared()
3) onVideoSizeChanged() - правильная высота и ширина

Вы не можете вызвать seekTo в (1), так как игрок еще не подготовлен.

Как вы заметили, если вы вызываете seekTo в (2), медиаплеер выдает предупреждение «Попытка поиска в прошлом конце файла»

Вы получаете (3) только в том случае, если был вызван MediaPlayer.start(), но в этот момент вы можете успешно вызвать seekTo().

MediaPlayer mMediaPlayer = new MediaPlayer(); // + some initialisation code
boolean mVideoSizeIsSet = false;
boolean mMediaPlayerIsPrepared = false;

public void onPrepared(MediaPlayer mediaplayer) {
    Log.d(TAG, "onPrepared called");
    mMediaPlayerIsPrepared = true;

    if (mVideoSizeIsSet) {
        mMediaPlayer.seekTo();
    }

    mMediaPlayer.start()
}


public void onVideoSizeChanged(MediaPlayer mp, int width, int height) {
    Log.d(TAG, "onVideoSizeChanged called");

    if (width == 0 || height == 0) {
        Log.d(TAG, "invalid video width(" + width + ") or height(" + height + ")");
    } else {

        mVideoSizeIsSet = true;

        if (mMediaPlayerIsPrepared) {
            mMediaPlayer.seekTo();
        }
    }
}

(Лично мне не нравится использование логических охранников, но если вы посмотрите на образец медиаплеера, поставляемый с SDK, он делает что-то подобное).

Тестирование на различных устройствах/версиях ОС обеспечивает универсальное решение. Однако в 4.2 есть ошибка. Вызов mMediaPlayer.start(), по-видимому, приводит к тому, что первые несколько кадров видео начинают воспроизводиться до того, как происходит seekTo(), это едва заметно для моей ситуации, но может быть более заметно для вас. В настоящее время я пытаюсь каким-то образом скрыть свое представление поверхности, пока не получу событие onSeekComplete(), но это не идеально.

Если у кого-то есть лучшее решение, которое работает во всех версиях ОС, я бы хотел его услышать.

person Simon    schedule 11.04.2013
comment
Следуя ответу Робина, я попытался отследить исходный код медиаплеера до того места, где в конечном итоге устанавливается значение продолжительности. В итоге я остановился на классе StageFrightPlayer, который имеет структуру AwesomePlayer, которая, похоже, содержит значение в своем члене mDurationUs. Кажется, это устанавливается, когда делается вызов либо initAudioDecoder, либо initVideoDecoder. Я предполагаю, что идеальное решение приведет к вызову одной из этих двух функций без необходимости вызывать функцию java MediaPlayer.start()? - person Simon; 11.04.2013
comment
Я также только что добавил эту информацию в качестве комментария к ошибке 54112< /а>. - person Simon; 11.04.2013

Это проблема с реализацией NuPlayer. NuPlayer не имеет надлежащей реализации состояния подготовки, в отличие от локального воспроизведения с AwesomePlayer. Сетевое соединение устанавливается только при вызове start(), после чего известна продолжительность. Проблема может быть решена путем обхода проверки длительности, если длительность равна 0, я пробовал это раньше.

person Surajit Podder    schedule 07.06.2013

Вот исходный код:

418 status_t MediaPlayer::seekTo_l(int msec)
419 {
420     ALOGV("seekTo %d", msec);
421     if ((mPlayer != 0) && ( mCurrentState & ( MEDIA_PLAYER_STARTED | MEDIA_PLAYER_PREPARED | MEDIA_PLAYER_PAUSED |  MEDIA_PLAYER_PLAYBACK_COMPLETE) ) ) {
422         if ( msec < 0 ) {
423             ALOGW("Attempt to seek to invalid position: %d", msec);
424             msec = 0;
425         } else if ((mDuration > 0) && (msec > mDuration)) {
426             ALOGW("Attempt to seek to past end of file: request = %d, EOF = %d", msec, mDuration);
427             msec = mDuration;
428         }
429         // cache duration
430         mCurrentPosition = msec;
431         if (mSeekPosition < 0) {
432             getDuration_l(NULL);
433             mSeekPosition = msec;
434             return mPlayer->seekTo(msec);
435         }
436         else {
437             ALOGV("Seek in progress - queue up seekTo[%d]", msec);
438             return NO_ERROR;
439         }
440     }
441     ALOGE("Attempt to perform seekTo in wrong state: mPlayer=%p, mCurrentState=%u", mPlayer.get(), mCurrentState);
442     return INVALID_OPERATION;
443 }

Я предлагаю вам сначала вызвать getDuration(), чтобы позволить экземпляру медиаплеера принудительно инициализировать поле продолжительности. В противном случае попробуйте даже вызвать start() перед seekTo() или даже вызвать seekTo() через некоторое время после start().

person Robin    schedule 09.04.2013
comment
спасибо за ответ, к сожалению, вызов getDuration() перед seekTo не помогает, start() перед seekTo() тоже не помогает, а вызов seekTo с задержкой не является надежным решением. - person Lukas Hanacek; 10.04.2013