Question

my problem seems to happen only on android 4.2.2

I go this way idle state -> initialized state -> prepareAsync() -> and in onPrepared the seekTo is called, but on this android version the mediaplayer returns

"Attempt to seek to past end of file: request = {int>0}, durationMs = 0"

and start to play from beginning, as there is no point where I could catch this, nor any listener, it just write this message to the log, I cannot really react on that.

What is even stranger is that if I call mediaPlayer.getDuration() in onPrepared() it returns proper value and not 0.

Do you think it is a mediaplayer bug or is there a better place where to call seekTo ? or maybe a way how to know the seekTo failed ? I would like to avoid periodically check current position if it is smaller than desired position and try to call seek to as this approach has a lot of different problems.

it is a smooth streaming video content

Était-ce utile?

La solution

I'm currently trying to find a solution to the same problem. The best I've come up with so far is as follows.

On 4.2 I've noticed that the following call backs are received:

1) onVideoSizeChanged() - where height and width = 0
2) onPrepared()
3) onVideoSizeChanged() - proper heights and widths

You can't call seekTo in (1) as the player is not yet prepared.

As you noted, if you call seekTo in (2) the media player generates the warning "Attempt to seek to past end of file"

You only receive (3) if MediaPlayer.start() has been called, but at this point you can call seekTo() successfully.

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();
        }
    }
}

(I personally don't like the use of the boolean guards, but if you look at the media player sample provided with the sdk, it does something similar).

Testing across a range of devices/OS versions, this provides a generic solution. However, there is a bug with 4.2. The call to mMediaPlayer.start() seems to cause the first few frames of the video start playing before the seekTo() occurs, this is barely visible for my situation, but may be more visible for you. I'm currently looking into hiding my surface view in some way until I receive the onSeekComplete() event, but this isn't ideal.

If anyone has a better solution that works across all OS versions I'd love to hear it.

Autres conseils

This is a problem with NuPlayer implementation. NuPlayer does not have a proper preparing state implementation, unlike local playback with AwesomePlayer. The network connection is established only when start() is called, after which the duration is known. The problem can be solved by bypassing the duration check if duration is 0, I have tried it before.

Here is the source code:

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 }

I suggest you to call getDuration() first to let the mediaplayer instance to force initialize the duration field. Otherwise, please even try to call start() before seekTo(), or even call seekTo() some time after start().

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top