android使用 mediaPlayer 播放video视频过程中, 当用户退出当前播放,再从后台恢复播放时,需要跳转到之前退出的时间点继续播放。使用的方法基本都是 SeekTo 之前的时间点,但是经常遇到恢复播放时位置不准的问题,而且甚至有重头开始播放的现象。这个是因为SeekTo是回到上一时间点附近的关键帧导致的。
针对这个问题,在最新的android 8.0平台上,已经有了新的解决方案:
SeekTo() 方法在android O 平台新增了一个多参数的方法:
void seekTo(long msec, @SeekMode int mode)
这里的Mode 传入
int SEEK_CLOSEST = 0x03
就不会出现播放视频位置不准确的现象了。
* Seek modes used in method seekTo(long, int) to move media position
* to a specified location.
* Do not change these mode values without updating their counterparts
* in include/media/IMediaSource.h!
* This mode is used with {
@link
#seekTo(long, int)} to move media position to
* a sync (or key) frame associated with a data source that is located
* right before or at the given time.
*
@see
#seekTo(long, int)
public
static
final
int
SEEK_PREVIOUS_SYNC = 0x00
;
* This mode is used with {
@link
#seekTo(long, int)} to move media position to
* a sync (or key) frame associated with a data source that is located
* right after or at the given time.
*
@see
#seekTo(long, int)
public
static
final
int
SEEK_NEXT_SYNC = 0x01
;
* This mode is used with {
@link
#seekTo(long, int)} to move media position to
* a sync (or key) frame associated with a data source that is located
* closest to (in time) or at the given time.
*
@see
#seekTo(long, int)
public
static
final
int
SEEK_CLOSEST_SYNC = 0x02
;
* This mode is used with {
@link
#seekTo(long, int)} to move media position to
* a frame (not necessarily a key frame) associated with a data source that
* is located closest to or at the given time.
*
@see
#seekTo(long, int)
public
static
final
int
SEEK_CLOSEST = 0x03
;
/**
@hide
*/
@IntDef(
value
=
{
SEEK_PREVIOUS_SYNC,
SEEK_NEXT_SYNC,
SEEK_CLOSEST_SYNC,
SEEK_CLOSEST,
@Retention(RetentionPolicy.SOURCE)
public
@
interface
SeekMode {}
private
native
final
void
_seekTo(
long
msec,
int
mode);
* Moves the media to specified time position by considering the given mode.
* When seekTo is finished, the user will be notified via OnSeekComplete supplied by the user.
* There is at most one active seekTo processed at any time. If there is a to-be-completed
* seekTo, new seekTo requests will be queued in such a way that only the last request
* is kept. When current seekTo is completed, the queued request will be processed if
* that request is different from just-finished seekTo operation, i.e., the requested
* position or mode is different.
*
@param
msec the offset in milliseconds from the start to seek to.
* When seeking to the given time position, there is no guarantee that the data source
* has a frame located at the position. When this happens, a frame nearby will be rendered.
* If msec is negative, time position zero will be used.
* If msec is larger than duration, duration will be used.
*
@param
mode the mode indicating where exactly to seek to.
* Use {
@link
#SEEK_PREVIOUS_SYNC} if one wants to seek to a sync frame
* that has a timestamp earlier than or the same as msec. Use
* {
@link
#SEEK_NEXT_SYNC} if one wants to seek to a sync frame
* that has a timestamp later than or the same as msec. Use
* {
@link
#SEEK_CLOSEST_SYNC} if one wants to seek to a sync frame
* that has a timestamp closest to or the same as msec. Use
* {
@link
#SEEK_CLOSEST} if one wants to seek to a frame that may
* or may not be a sync frame but is closest to or the same as msec.
* {
@link
#SEEK_CLOSEST} often has larger performance overhead compared
* to the other options if there is no sync frame located at msec.
*
@throws
IllegalStateException if the internal player engine has not been
* initialized
*
@throws
IllegalArgumentException if the mode is invalid.
public
void
seekTo(
long
msec, @SeekMode
int
mode) {
if
(mode < SEEK_PREVIOUS_SYNC || mode >
SEEK_CLOSEST) {
final
String msg = "Illegal seek mode: " +
mode;
throw
new
IllegalArgumentException(msg);
//
TODO: pass long to native, instead of truncating here.
if
(msec >
Integer.MAX_VALUE) {
Log.w(TAG,
"seekTo offset " + msec + " is too large, cap to " +
Integer.MAX_VALUE);
msec
=
Integer.MAX_VALUE;
}
else
if
(msec <
Integer.MIN_VALUE) {
Log.w(TAG,
"seekTo offset " + msec + " is too small, cap to " +
Integer.MIN_VALUE);
msec
=
Integer.MIN_VALUE;
_seekTo(msec, mode);