1. Seeking功能的相关接口
通过双精度时间寻找跳转:
- Seek() ? 跳转到精确的指定时间上的画面
- SeekFast() ? 跳转到指定时间上最近的帧画面
- SeekWithTolerance() ? 跳转到指定时间上某个范围时间内的画面(只支持macOS,ios,tvOS)
通过使用帧寻找跳转(只对具有已知恒定帧速率的媒体有效):
- SeekToFrame() ? 跳转到具体某一帧的画面
- SeekToFrameRelative() ? 跳转到相对于当前帧向前或向后多少帧的画面
底层接口源码如下:
void Seek(double time);
void SeekFast(double time);
void SeekWithTolerance(double time, double timeDeltaBefore, double timeDeltaAfter);
void SeekToFrame(int frame, float overrideFrameRate = 0f);
void SeekToFrameRelative(int frameOffset, float overrideFrameRate = 0f);
需要注意的是,跳转的响应/行为在不同平台会有不同差异
平台 | 快速近似关键帧搜索(Fast Approximate Keyframe Seeking) | 慢速精确寻帧(Slow Accurate Frame Seeking) |
---|
Windows (WinRT / Media Foundation) | ? | ? | Windows (DirectShow) | ? | 取决于编解码器 | Android (ExoPlayer) | ? | ? | Android (MediaPlayer) | ? | API 26 及以上 | macOS | ? | ? | iOS/iPadOS/tvOS | ? | ? | WebGL | ? | 视情况而变化 |
2. 跳转(Seeking)功能的实现案例
具体的实现可以参考AVPro提供的Demo(Demo_MediaPlayer场景)
下面只列出结合时间条(slider)进行视频跳转的部分:
[Header("UI Components")]
[SerializeField] Slider _sliderTime = null;
void Start()
{
CreateTimelineDragEvents();
}
private void CreateTimelineDragEvents()
{
EventTrigger trigger = _sliderTime.gameObject.GetComponent<EventTrigger>();
if (trigger != null)
{
EventTrigger.Entry entry = new EventTrigger.Entry();
entry.eventID = EventTriggerType.PointerDown;
entry.callback.AddListener((data) => { OnTimeSliderBeginDrag(); });
trigger.triggers.Add(entry);
entry = new EventTrigger.Entry();
entry.eventID = EventTriggerType.Drag;
entry.callback.AddListener((data) => { OnTimeSliderDrag(); });
trigger.triggers.Add(entry);
entry = new EventTrigger.Entry();
entry.eventID = EventTriggerType.PointerUp;
entry.callback.AddListener((data) => { OnTimeSliderEndDrag(); });
trigger.triggers.Add(entry);
entry = new EventTrigger.Entry();
entry.eventID = EventTriggerType.PointerEnter;
entry.callback.AddListener((data) => { OnTimelineBeginHover((PointerEventData)data); });
trigger.triggers.Add(entry);
entry = new EventTrigger.Entry();
entry.eventID = EventTriggerType.PointerExit;
entry.callback.AddListener((data) => { OnTimelineEndHover((PointerEventData)data); });
trigger.triggers.Add(entry);
}
}
private bool _wasPlayingBeforeTimelineDrag;
private void OnTimeSliderBeginDrag()
{
if (_mediaPlayer && _mediaPlayer.Control != null)
{
_wasPlayingBeforeTimelineDrag = _mediaPlayer.Control.IsPlaying();
if (_wasPlayingBeforeTimelineDrag)
{
_mediaPlayer.Pause();
}
OnTimeSliderDrag();
}
}
private void OnTimeSliderDrag()
{
if (_mediaPlayer && _mediaPlayer.Control != null)
{
TimeRange timelineRange = GetTimelineRange();
double time = timelineRange.startTime + (_sliderTime.value * timelineRange.duration);
_mediaPlayer.Control.Seek(time);
_isHoveringOverTimeline = true;
}
}
private void OnTimeSliderEndDrag()
{
if (_mediaPlayer && _mediaPlayer.Control != null)
{
if (_wasPlayingBeforeTimelineDrag)
{
_mediaPlayer.Play();
_wasPlayingBeforeTimelineDrag = false;
}
}
}
private bool _isHoveringOverTimeline;
private void OnTimelineBeginHover(PointerEventData eventData)
{
if (eventData.pointerCurrentRaycast.gameObject != null)
{
_isHoveringOverTimeline = true;
_sliderTime.transform.localScale = new Vector3(1f, 2.5f, 1f);
}
}
private void OnTimelineEndHover(PointerEventData eventData)
{
_isHoveringOverTimeline = false;
_sliderTime.transform.localScale = new Vector3(1f, 1f, 1f);
}
参考文献
AVPro官方说明文档
|