Unity播放器插件-AVPro Video - Ultra Edition介绍

这篇具有很好参考价值的文章主要介绍了Unity播放器插件-AVPro Video - Ultra Edition介绍。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

c# AVPro Video - Ultra Edition官方插件使用介绍
AvproVideo,官网中有免费的,免费的带了水印。不介意水印去下载,挺好使的。收费的在Unity商店中能找到:(这里我从淘宝上花“重金”买了一个玩玩)以前都是用的AvPro免费版的,1点几那种版本的,也挺好使,相对这个来说,这个就是某东版本的,那个是拼夕夕版本的。
avpro unity,Unity,unity,java,游戏引擎

常用API

获取MediaPlayer组件,使用代码如下:

MediaPlayer meida;
//播放
media.Control.Play();
//暂停
media.Control.Pause();
//停止
media.Control.Stop();
//重播
media.Control.Rewind();
//设置播放链接AbsolutePathOrURL是Url的形式,可以选择文件夹的形式,设置对应文件夹属性即可
media.OpenMedia(MediaPathType.AbsolutePathOrURL,"视频链接",true);
//StreamingAssets文件目录下的视频地址
media.OpenMedia(MediaPathType.RelativeToStreamingAssetsFolder,"视频名称",true);
//事件监听
media.Events.AddListener(OnVideoEvent); 
private void OnVideoEvent(MediaPlayer arg0, MediaPlayerEvent.EventType arg1, ErrorCode arg2)
        {
            switch (arg1)
            {
                case MediaPlayerEvent.EventType.MetaDataReady:
                    break;
                case MediaPlayerEvent.EventType.ReadyToPlay: 
                    break;
                case MediaPlayerEvent.EventType.Started: 
                    break;
                case MediaPlayerEvent.EventType.FirstFrameReady:
                    break;
                case MediaPlayerEvent.EventType.FinishedPlaying:
                    print("无循环状态下播放完成");
                    break;
                case MediaPlayerEvent.EventType.Closing:
                    print(".Closing...");
                    break;
                case MediaPlayerEvent.EventType.Error:
                    print(".Error...");
                    break;
                case MediaPlayerEvent.EventType.SubtitleChange:
                    break;
                case MediaPlayerEvent.EventType.Stalled:
                    break;
                case MediaPlayerEvent.EventType.Unstalled:
                    break;
                case MediaPlayerEvent.EventType.ResolutionChanged:
                    break;
                case MediaPlayerEvent.EventType.StartedSeeking:
                    break;
                case MediaPlayerEvent.EventType.FinishedSeeking:
                    print("循环设置状态下播放结束...");
                    break;
                case MediaPlayerEvent.EventType.StartedBuffering:
                    break;
                case MediaPlayerEvent.EventType.FinishedBuffering: 
                    break;
                case MediaPlayerEvent.EventType.PropertiesChanged:
                    break;
                case MediaPlayerEvent.EventType.PlaylistItemChanged:
                    break;
                case MediaPlayerEvent.EventType.PlaylistFinished:
                    break;
                case MediaPlayerEvent.EventType.TextTracksChanged:
                    break;
                //case MediaPlayerEvent.EventType.TextCueChanged://=>  SubtitleChange
                //  break;
                default:
                    break;
            }
        }

Demo源码

该插件有很详细的demo演示场景,很容易使用,基本功能都已封装好,直接使用就行。以下是我将它封装好的功能写成一个单列。

using RenderHeads.Media.AVProVideo.Demos.UI;
using RenderHeads.Media.AVProVideo;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
namespace Demo
{
    public class MediaManager : MonoBehaviour
    {
        public static MediaManager m_Ins;
        [SerializeField] MediaPlayer _mediaPlayer = null; 
        [Header("Options")] 
        [SerializeField] float _keyVolumeDelta = 0.05f;
        [SerializeField] float _jumpDeltaTime = 5f;
        [SerializeField] bool _autoHide = true;
        [SerializeField] float _userInactiveDuration = 1.5f;
        [SerializeField] bool _useAudioFading = true; 
        [Header("Keyboard Controls")]
        [SerializeField] bool _enableKeyboardControls = true;
        [SerializeField] KeyCode KeyVolumeUp = KeyCode.UpArrow;
        [SerializeField] KeyCode KeyVolumeDown = KeyCode.DownArrow;
        [SerializeField] KeyCode KeyTogglePlayPause = KeyCode.Space;
        [SerializeField] KeyCode KeyToggleMute = KeyCode.M;
        [SerializeField] KeyCode KeyJumpForward = KeyCode.RightArrow;
        [SerializeField] KeyCode KeyJumpBack = KeyCode.LeftArrow;

        [Header("Optional Components")]
        [SerializeField] OverlayManager _overlayManager = null;
        [SerializeField] MediaPlayer _thumbnailMediaPlayer = null;
        [SerializeField] RectTransform _timelineTip = null;

        [Header("UI Components")]
        [SerializeField] RectTransform _canvasTransform = null;
        //[SerializeField] Image image = null;
        [SerializeField] Slider _sliderTime = null;
        [SerializeField] EventTrigger _videoTouch = null;
        [SerializeField] CanvasGroup _controlsGroup = null;

        [Header("UI Components (Optional)")]
        [SerializeField] GameObject _liveItem = null;
        [SerializeField] Text _textMediaName = null;
        [SerializeField] Text _textTimeDuration = null;
        [SerializeField] Slider _sliderVolume = null;
        [SerializeField] Button _buttonPlayPause = null;
        [SerializeField] Button _buttonVolume = null;
        [SerializeField] Button _buttonSubtitles = null;
        [SerializeField] Button _buttonOptions = null;
        [SerializeField] Button _buttonTimeBack = null;
        [SerializeField] Button _buttonTimeForward = null;
        [SerializeField] RawImage _imageAudioSpectrum = null;
        [SerializeField] HorizontalSegmentsPrimitive _segmentsSeek = null;
        [SerializeField] HorizontalSegmentsPrimitive _segmentsBuffered = null;
        [SerializeField] HorizontalSegmentsPrimitive _segmentsProgress = null;

        private bool _wasPlayingBeforeTimelineDrag;
        private float _controlsFade = 1f;
        private Material _playPauseMaterial;
        private Material _volumeMaterial;
        private Material _subtitlesMaterial;
        private Material _audioSpectrumMaterial;
        private float[] _spectrumSamples = new float[128];
        private float[] _spectrumSamplesSmooth = new float[128];
        private float _maxValue = 1f;
        private float _audioVolume = 1f;

        private float _audioFade = 0f;
        private bool _isAudioFadingUpToPlay = true;
        private const float AudioFadeDuration = 0.25f;
        private float _audioFadeTime = 0f;

        private readonly LazyShaderProperty _propMorph = new LazyShaderProperty("_Morph");
        private readonly LazyShaderProperty _propMute = new LazyShaderProperty("_Mute");
        private readonly LazyShaderProperty _propVolume = new LazyShaderProperty("_Volume");
        private readonly LazyShaderProperty _propSpectrum = new LazyShaderProperty("_Spectrum");
        private readonly LazyShaderProperty _propSpectrumRange = new LazyShaderProperty("_SpectrumRange");

        void Awake()
        {
            m_Ins = this;
#if UNITY_IOS
			Application.targetFrameRate = 60;
#endif
        }

        void Start()
        {
            if (_mediaPlayer)
            {
                _audioVolume = _mediaPlayer.AudioVolume;
            }
            SetupPlayPauseButton();
            SetupTimeBackForwardButtons();
            SetupVolumeButton();
            SetupSubtitlesButton();
            SetupAudioSpectrum();
            CreateTimelineDragEvents();
            CreateVideoTouchEvents();
            CreateVolumeSliderEvents();
            UpdateVolumeSlider();
        }

        private struct UserInteraction
        {
            public static float InactiveTime;
            private static Vector3 _previousMousePos;
            private static int _lastInputFrame;

            public static bool IsUserInputThisFrame()
            {
                if (Time.frameCount == _lastInputFrame)
                {
                    return true;
                }
                bool touchInput = (Input.touchSupported && Input.touchCount > 0);
                bool mouseInput = (Input.mousePresent && (Input.mousePosition != _previousMousePos || Input.mouseScrollDelta != Vector2.zero || Input.GetMouseButton(0)));

                if (touchInput || mouseInput)
                {
                    _previousMousePos = Input.mousePosition;
                    _lastInputFrame = Time.frameCount;
                    return true;
                }

                return false;
            }
        }

        private Material DuplicateMaterialOnImage(Graphic image)
        {
            // Assign a copy of the material so we aren't modifying the material asset file
            image.material = new Material(image.material);
            return image.material;
        }

        private void SetupPlayPauseButton()
        {
            if (_buttonPlayPause)
            {
                _buttonPlayPause.onClick.AddListener(OnPlayPauseButtonPressed);
                _playPauseMaterial = DuplicateMaterialOnImage(_buttonPlayPause.GetComponent<Image>());
            }
        }
        /// <summary>
        /// 视频播放链接设置
        /// </summary>
        /// <param name="url"></param>
        public void SetMediaUrl(string url, MediaPathType type = MediaPathType.AbsolutePathOrURL)
        {
#if UNITY_EDITOR || UNITY_WIN
            _mediaPlayer.OpenMedia(type, url, true);
#elif UNITY_ANDROID
            _mediaPlayer.OpenMedia(type, url, true);
#endif
        }
        public MediaPlayer GetMediaPlayer { get { 
                return _mediaPlayer; 
            }
        }
        private void SetupTimeBackForwardButtons()
        {
            if (_buttonTimeBack)
            {
                _buttonTimeBack.onClick.AddListener(OnPlayTimeBackButtonPressed);
            }
            if (_buttonTimeForward)
            {
                _buttonTimeForward.onClick.AddListener(OnPlayTimeForwardButtonPressed);
            }
        }

        private void SetupVolumeButton()
        {
            if (_buttonVolume)
            {
                _buttonVolume.onClick.AddListener(OnVolumeButtonPressed);
                _volumeMaterial = DuplicateMaterialOnImage(_buttonVolume.GetComponent<Image>());
            }
        }

        private void SetupSubtitlesButton()
        {
            if (_buttonSubtitles)
            {
                _buttonSubtitles.onClick.AddListener(OnSubtitlesButtonPressed);
                _subtitlesMaterial = DuplicateMaterialOnImage(_buttonSubtitles.GetComponent<Image>());
            }
        }

        private void SetupAudioSpectrum()
        {
            if (_imageAudioSpectrum)
            {
                _audioSpectrumMaterial = DuplicateMaterialOnImage(_imageAudioSpectrum);
            }
        }

        private void OnPlayPauseButtonPressed()
        {
            TogglePlayPause();
        }

        private void OnPlayTimeBackButtonPressed()
        {
            SeekRelative(-_jumpDeltaTime);
        }

        private void OnPlayTimeForwardButtonPressed()
        {
            SeekRelative(_jumpDeltaTime);
        }

        private void OnVolumeButtonPressed()
        {
            ToggleMute();
        }

        private void OnSubtitlesButtonPressed()
        {
            ToggleSubtitles();
        }

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

        private void CreateVideoTouchEvents()
        {
            EventTrigger.Entry entry = new EventTrigger.Entry();
            entry.eventID = EventTriggerType.PointerUp;
            entry.callback.AddListener((data) => { OnVideoPointerUp(); });
            _videoTouch.triggers.Add(entry);
        }

        private void OnVideoPointerUp()
        {
            bool controlsMostlyVisible = (_controlsGroup.alpha >= 0.5f && _controlsGroup.gameObject.activeSelf);
            if (controlsMostlyVisible)
            {
                TogglePlayPause();
            }
        }

        void UpdateAudioFading()
        {
            // Increment fade timer
            if (_audioFadeTime < AudioFadeDuration)
            {
                _audioFadeTime = Mathf.Clamp(_audioFadeTime + Time.deltaTime, 0f, AudioFadeDuration);
            }

            // Trigger pause when audio faded down
            if (_audioFadeTime >= AudioFadeDuration)
            {
                if (!_isAudioFadingUpToPlay)
                {
                    Pause(skipFeedback: true);
                }
            }

            // Apply audio fade value
            if (_mediaPlayer.Control != null && _mediaPlayer.Control.IsPlaying())
            {
                _audioFade = Mathf.Clamp01(_audioFadeTime / AudioFadeDuration);
                if (!_isAudioFadingUpToPlay)
                {
                    _audioFade = (1f - _audioFade);
                }
                ApplyAudioVolume();
            }
        }

        public void TogglePlayPause()
        {
            if (_mediaPlayer && _mediaPlayer.Control != null)
            {
                if (_useAudioFading && _mediaPlayer.Info.HasAudio())
                {
                    if (_mediaPlayer.Control.IsPlaying())
                    {
                        if (_overlayManager)
                        {
                            _overlayManager.TriggerFeedback(OverlayManager.Feedback.Pause);
                        }
                        _isAudioFadingUpToPlay = false;
                    }
                    else
                    {
                        _isAudioFadingUpToPlay = true;
                        Play();
                    }
                    _audioFadeTime = 0f;
                }
                else
                {
                    if (_mediaPlayer.Control.IsPlaying())
                    {
                        Pause();
                    }
                    else
                    {
                        Play();
                    }
                }
            }
        }

        public void Play()
        {
            if (_mediaPlayer && _mediaPlayer.Control != null)
            {
                if (_overlayManager)
                {
                    _overlayManager.TriggerFeedback(OverlayManager.Feedback.Play);
                }
                _mediaPlayer.Play();
            }
        }

        public void Pause(bool skipFeedback = false)
        {
            if (_mediaPlayer && _mediaPlayer.Control != null)
            {
                if (!skipFeedback)
                {
                    if (_overlayManager)
                    {
                        _overlayManager.TriggerFeedback(OverlayManager.Feedback.Pause);
                    }
                }
                _mediaPlayer.Pause();
            }
        }
        public void Stop()
        {
            if (_mediaPlayer && _mediaPlayer.Control != null)
            {  
                _mediaPlayer.Stop();
                _mediaPlayer.CloseMedia();
            }
        }
        public void Rewind()
        {
            if (_mediaPlayer && _mediaPlayer.Control != null)
            {
                if (_overlayManager)
                {
                    _overlayManager.TriggerFeedback(OverlayManager.Feedback.Play);
                }
                _mediaPlayer.Rewind(true);
                _mediaPlayer.Play();
            }
        }
        public void SeekRelative(float deltaTime)
        {
            if (_mediaPlayer && _mediaPlayer.Control != null)
            {
                TimeRange timelineRange = GetTimelineRange();
                double time = _mediaPlayer.Control.GetCurrentTime() + deltaTime;
                time = System.Math.Max(time, timelineRange.startTime);
                time = System.Math.Min(time, timelineRange.startTime + timelineRange.duration);
                _mediaPlayer.Control.Seek(time);

                if (_overlayManager)
                {
                    _overlayManager.TriggerFeedback(deltaTime > 0f ? OverlayManager.Feedback.SeekForward : OverlayManager.Feedback.SeekBack);
                }
            }
        }

        public void ChangeAudioVolume(float delta)
        {
            if (_mediaPlayer && _mediaPlayer.Control != null)
            {
                // Change volume
                _audioVolume = Mathf.Clamp01(_audioVolume + delta);

                // Update the UI
                UpdateVolumeSlider();

                // Trigger the overlays
                if (_overlayManager)
                {
                    _overlayManager.TriggerFeedback(delta > 0f ? OverlayManager.Feedback.VolumeUp : OverlayManager.Feedback.VolumeDown);
                }
            }
        }

        public void ToggleMute()
        {
            if (_mediaPlayer && _mediaPlayer.Control != null)
            {
                if (_mediaPlayer.Control.IsMuted())
                {
                    MuteAudio(false);
                }
                else
                {
                    MuteAudio(true);
                }
            }
        }

        public void MuteAudio(bool mute)
        {
            if (_mediaPlayer && _mediaPlayer.Control != null)
            {
                // Change mute
                _mediaPlayer.Control.MuteAudio(mute);

                // Update the UI
                // The UI element is constantly updated by the Update() method

                // Trigger the overlays
                if (_overlayManager)
                {
                    _overlayManager.TriggerFeedback(mute ? OverlayManager.Feedback.VolumeMute : OverlayManager.Feedback.VolumeUp);
                }
            }
        }

        public void ToggleSubtitles()
        {
            if (_mediaPlayer && _mediaPlayer.TextTracks != null)
            {
                if (_mediaPlayer.TextTracks.GetTextTracks().Count > 0)
                {
                    if (_mediaPlayer.TextTracks.GetActiveTextTrack() != null)
                    {
                        _mediaPlayer.TextTracks.SetActiveTextTrack(null);
                    }
                    else
                    {
                        // TODO: instead of activating the first one, base it on the language/track 
                        // selection stored in the MediaPlayerUI
                        _mediaPlayer.TextTracks.SetActiveTextTrack(_mediaPlayer.TextTracks.GetTextTracks()[0]);
                    }
                }
            }
        }

        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 void CreateVolumeSliderEvents()
        {
            if (_sliderVolume != null)
            {
                EventTrigger trigger = _sliderVolume.gameObject.GetComponent<EventTrigger>();
                if (trigger != null)
                {
                    EventTrigger.Entry entry = new EventTrigger.Entry();
                    entry.eventID = EventTriggerType.PointerDown;
                    entry.callback.AddListener((data) => { OnVolumeSliderDrag(); });
                    trigger.triggers.Add(entry);

                    entry = new EventTrigger.Entry();
                    entry.eventID = EventTriggerType.Drag;
                    entry.callback.AddListener((data) => { OnVolumeSliderDrag(); });
                    trigger.triggers.Add(entry);
                }
            }
        }

        private void OnVolumeSliderDrag()
        {
            if (_mediaPlayer && _mediaPlayer.Control != null)
            {
                _audioVolume = _sliderVolume.value;
                ApplyAudioVolume();
            }
        }

        private void ApplyAudioVolume()
        {
            if (_mediaPlayer)
            {
                _mediaPlayer.AudioVolume = (_audioVolume * _audioFade);
            }
        }

        private void UpdateVolumeSlider()
        {
            if (_sliderVolume)
            {
                if (_mediaPlayer)
                {
                    // TODO: remove this
                    /*if (mp.Control != null)
                    {
                        _sliderVolume.value = mp.Control.GetVolume();
                    }
                    else*/
                    {
                        _sliderVolume.value = _audioVolume;
                    }
                }
            }
        }

        private void UpdateAudioSpectrum()
        {
            bool showAudioSpectrum = false;
            if (_mediaPlayer && _mediaPlayer.Control != null)
            {
                AudioSource audioSource = _mediaPlayer.AudioSource;
                if (audioSource && _audioSpectrumMaterial)
                {
                    showAudioSpectrum = true;

                    float maxFreq = (Helper.GetUnityAudioSampleRate() / 2);

                    // Frequencies over 18Khz generally aren't very interesting to visualise, so clamp the range
                    const float clampFreq = 18000f;
                    int sampleRange = Mathf.FloorToInt(Mathf.Clamp01(clampFreq / maxFreq) * _spectrumSamples.Length);

                    // Add new samples and smooth the samples over time
                    audioSource.GetSpectrumData(_spectrumSamples, 0, FFTWindow.BlackmanHarris);

                    // Find the maxValue sample for normalising with
                    float maxValue = -1.0f;
                    for (int i = 0; i < sampleRange; i++)
                    {
                        if (_spectrumSamples[i] > maxValue)
                        {
                            maxValue = _spectrumSamples[i];
                        }
                    }

                    // Chase maxValue to zero
                    _maxValue = Mathf.Lerp(_maxValue, 0.0f, Mathf.Clamp01(2.0f * Time.deltaTime));

                    // Update maxValue
                    _maxValue = Mathf.Max(_maxValue, maxValue);
                    if (_maxValue <= 0.01f)
                    {
                        _maxValue = 1f;
                    }

                    // Copy and smooth the spectrum values
                    for (int i = 0; i < sampleRange; i++)
                    {
                        float newSample = _spectrumSamples[i] / _maxValue;
                        _spectrumSamplesSmooth[i] = Mathf.Lerp(_spectrumSamplesSmooth[i], newSample, Mathf.Clamp01(15.0f * Time.deltaTime));
                    }

                    // Update shader
                    _audioSpectrumMaterial.SetFloatArray(_propSpectrum.Id, _spectrumSamplesSmooth);
                    _audioSpectrumMaterial.SetFloat(_propSpectrumRange.Id, (float)sampleRange);
                }
            }

            if (_imageAudioSpectrum)
            {
                _imageAudioSpectrum.gameObject.SetActive(showAudioSpectrum);
            }
        }

        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 TimeRange GetTimelineRange()
        {
            if (_mediaPlayer.Info != null)
            {
                return Helper.GetTimelineRange(_mediaPlayer.Info.GetDuration(), _mediaPlayer.Control.GetSeekableTimes());
            }
            return new TimeRange();
        }

        private bool CanHideControls()
        {
            bool result = true;
            if (!_autoHide)
            {
                result = false;
            }
            else if (Input.mousePresent)
            {
                // Check whether the mouse cursor is over the controls, in which case we can't hide the UI
                RectTransform rect = _controlsGroup.GetComponent<RectTransform>();
                Vector2 canvasPos;
                RectTransformUtility.ScreenPointToLocalPointInRectangle(rect, Input.mousePosition, null, out canvasPos);

                Rect rr = RectTransformUtility.PixelAdjustRect(rect, null);
                result = !rr.Contains(canvasPos);
            }
            return result;
        }

        private void UpdateControlsVisibility()
        {
            if (UserInteraction.IsUserInputThisFrame() || !CanHideControls())
            {
                UserInteraction.InactiveTime = 0f;
                FadeUpControls();
            }
            else
            {

                UserInteraction.InactiveTime += Time.unscaledDeltaTime;
                if (UserInteraction.InactiveTime >= _userInactiveDuration)
                {
                    FadeDownControls();
                }
                else
                {
                    FadeUpControls();
                }
            }
        }

        private void FadeUpControls()
        {
            if (!_controlsGroup.gameObject.activeSelf)
            {
                _controlsGroup.gameObject.SetActive(true);
            }
            _controlsFade = Mathf.Min(1f, _controlsFade + Time.deltaTime * 8f);
            _controlsGroup.alpha = Mathf.Pow(_controlsFade, 5f);
        }

        private void FadeDownControls()
        {
            if (_controlsGroup.gameObject.activeSelf)
            {
                _controlsFade = Mathf.Max(0f, _controlsFade - Time.deltaTime * 3f);
                _controlsGroup.alpha = Mathf.Pow(_controlsFade, 5f);
                if (_controlsGroup.alpha <= 0f)
                {
                    _controlsGroup.gameObject.SetActive(false);
                }
            }
        }

        void Update()
        {
            if (!_mediaPlayer) return;

            UpdateControlsVisibility();
            UpdateAudioFading();
            UpdateAudioSpectrum();

            if (_mediaPlayer.Info != null)
            {
                TimeRange timelineRange = GetTimelineRange();

                // Update timeline hover popup
                if (_timelineTip != null)
                {
                    if (_isHoveringOverTimeline)
                    {
                        Vector2 canvasPos;
                        RectTransformUtility.ScreenPointToLocalPointInRectangle(_canvasTransform, Input.mousePosition, null, out canvasPos);

                        _segmentsSeek.gameObject.SetActive(true);
                        _timelineTip.gameObject.SetActive(true);
                        Vector3 mousePos = _canvasTransform.TransformPoint(canvasPos);

                        _timelineTip.position = new Vector2(mousePos.x, _timelineTip.position.y);

                        if (UserInteraction.IsUserInputThisFrame())
                        {
                            // Work out position on the timeline
                            Bounds bounds = RectTransformUtility.CalculateRelativeRectTransformBounds(this._sliderTime.GetComponent<RectTransform>());
                            float x = Mathf.Clamp01((canvasPos.x - bounds.min.x) / bounds.size.x);

                            double time = (double)x * timelineRange.Duration;

                            // Seek to the new position
                            if (_thumbnailMediaPlayer != null && _thumbnailMediaPlayer.Control != null)
                            {
                                _thumbnailMediaPlayer.Control.SeekFast(time);
                            }

                            // Update time text
                            Text hoverText = _timelineTip.GetComponentInChildren<Text>();
                            if (hoverText != null)
                            {
                                time -= timelineRange.startTime;
                                time = System.Math.Max(time, 0.0);
                                time = System.Math.Min(time, timelineRange.Duration);
                                hoverText.text = Helper.GetTimeString(time, false);
                            }

                            {
                                // Update seek segment when hovering over timeline
                                if (_segmentsSeek != null)
                                {
                                    float[] ranges = new float[2];
                                    if (timelineRange.Duration > 0.0)
                                    {
                                        double t = ((_mediaPlayer.Control.GetCurrentTime() - timelineRange.startTime) / timelineRange.duration);
                                        ranges[1] = x;
                                        ranges[0] = (float)t;
                                    }
                                    _segmentsSeek.Segments = ranges;
                                }
                            }
                        }
                    }
                    else
                    {
                        _timelineTip.gameObject.SetActive(false);
                        _segmentsSeek.gameObject.SetActive(false);
                    }
                }

                // Updated stalled display
                if (_overlayManager)
                {
                    _overlayManager.Reset();
                    if (_mediaPlayer.Info.IsPlaybackStalled())
                    {
                        _overlayManager.TriggerStalled();
                    }
                }

                // Update keyboard input
                if (_enableKeyboardControls)
                {
                    // Keyboard toggle play/pause
                    if (Input.GetKeyDown(KeyTogglePlayPause))
                    {
                        TogglePlayPause();
                    }

                    // Keyboard seek 5 seconds
                    if (Input.GetKeyDown(KeyJumpBack))
                    {
                        SeekRelative(-_jumpDeltaTime);
                    }
                    else if (Input.GetKeyDown(KeyJumpForward))
                    {
                        SeekRelative(_jumpDeltaTime);
                    }

                    // Keyboard control volume
                    if (Input.GetKeyDown(KeyVolumeUp))
                    {
                        ChangeAudioVolume(_keyVolumeDelta);
                    }
                    else if (Input.GetKeyDown(KeyVolumeDown))
                    {
                        ChangeAudioVolume(-_keyVolumeDelta);
                    }

                    // Keyboard toggle mute
                    if (Input.GetKeyDown(KeyToggleMute))
                    {
                        ToggleMute();
                    }
                }

                // Animation play/pause button
                if (_playPauseMaterial != null)
                {
                    float t = _playPauseMaterial.GetFloat(_propMorph.Id);
                    float d = 1f;
                    if (_mediaPlayer.Control.IsPlaying())
                    {
                        d = -1f;
                    }
                    t += d * Time.deltaTime * 6f;
                    t = Mathf.Clamp01(t);
                    _playPauseMaterial.SetFloat(_propMorph.Id, t);
                }

                // Animation volume/mute button
                if (_volumeMaterial != null)
                {
                    float t = _volumeMaterial.GetFloat(_propMute.Id);
                    float d = 1f;
                    if (!_mediaPlayer.Control.IsMuted())
                    {
                        d = -1f;
                    }
                    t += d * Time.deltaTime * 6f;
                    t = Mathf.Clamp01(t);
                    _volumeMaterial.SetFloat(_propMute.Id, t);
                    _volumeMaterial.SetFloat(_propVolume.Id, _audioVolume);
                }

                // Animation subtitles button
                if (_subtitlesMaterial)
                {
                    float t = _subtitlesMaterial.GetFloat(_propMorph.Id);
                    float d = 1f;
                    if (_mediaPlayer.TextTracks.GetActiveTextTrack() == null)
                    {
                        d = -1f;
                    }
                    t += d * Time.deltaTime * 6f;
                    t = Mathf.Clamp01(t);
                    _subtitlesMaterial.SetFloat(_propMorph.Id, t);
                }

                // Update time/duration text display
                if (_textTimeDuration)
                {
                    string t1 = Helper.GetTimeString((_mediaPlayer.Control.GetCurrentTime() - timelineRange.startTime), false);
                    string d1 = Helper.GetTimeString(timelineRange.duration, false);
                    _textTimeDuration.text = string.Format("{0} / {1}", t1, d1);
                }

                // Update volume slider
                if (!_useAudioFading)
                {
                    UpdateVolumeSlider();
                }

                // Update time slider position
                if (_sliderTime)
                {
                    double t = 0.0;
                    if (timelineRange.duration > 0.0)
                    {
                        t = ((_mediaPlayer.Control.GetCurrentTime() - timelineRange.startTime) / timelineRange.duration);
                    }
                    _sliderTime.value = Mathf.Clamp01((float)t);
                }

                // Update LIVE text visible
                if (_liveItem)
                {
                    _liveItem.SetActive(double.IsInfinity(_mediaPlayer.Info.GetDuration()));
                }

                // Update subtitle button visible
                if (_buttonSubtitles)
                {
                    _buttonSubtitles.gameObject.SetActive(_mediaPlayer.TextTracks.GetTextTracks().Count > 0);
                }

                // Update media name
                if (_textMediaName)
                {
#if MEDIA_NAME
					string mediaName = string.Empty;
					if (!string.IsNullOrEmpty(_mediaPlayer.VideoPath))
					{
						mediaName  = System.IO.Path.GetFileName(_mediaPlayer.VideoPath);
						if (mediaName.Length > 26)
						{
							mediaName = mediaName.Substring(0, 26);
						}
					}
#endif

                    string resolutionName = string.Empty;
                    if (_mediaPlayer.Info.GetVideoWidth() > 0)
                    {
                        resolutionName = Helper.GetFriendlyResolutionName(_mediaPlayer.Info.GetVideoWidth(), _mediaPlayer.Info.GetVideoHeight(), _mediaPlayer.Info.GetVideoFrameRate());
                    }

#if MEDIA_NAME
					_textMediaName.text = string.Format("{0} {1}", mediaName, resolutionName);
#else
                    _textMediaName.text = resolutionName;
#endif
                }

                // Update buffered segments
                if (_segmentsBuffered)
                {
                    TimeRanges times = _mediaPlayer.Control.GetBufferedTimes();
                    float[] ranges = null;
                    if (times.Count > 0 && timelineRange.duration > 0.0)
                    {
                        ranges = new float[times.Count * 2];
                        for (int i = 0; i < times.Count; i++)
                        {
                            ranges[i * 2 + 0] = Mathf.Max(0f, (float)((times[i].StartTime - timelineRange.startTime) / timelineRange.duration));
                            ranges[i * 2 + 1] = Mathf.Min(1f, (float)((times[i].EndTime - timelineRange.startTime) / timelineRange.duration));
                        }
                    }
                    _segmentsBuffered.Segments = ranges;
                }

                // Update progress segment
                if (_segmentsProgress)
                {
                    TimeRanges times = _mediaPlayer.Control.GetBufferedTimes();
                    float[] ranges = null;
                    if (times.Count > 0 && timelineRange.Duration > 0.0)
                    {
                        ranges = new float[2];
                        double x1 = (times.MinTime - timelineRange.startTime) / timelineRange.duration;
                        double x2 = ((_mediaPlayer.Control.GetCurrentTime() - timelineRange.startTime) / timelineRange.duration);
                        ranges[0] = Mathf.Max(0f, (float)x1);
                        ranges[1] = Mathf.Min(1f, (float)x2);
                    }
                    _segmentsProgress.Segments = ranges;
                }
            }
        }
        #region GUI 显示一大堆几乎没用的
        //    void OnGUI()
        //    {
        //        // NOTE: These this IMGUI is just temporary until we implement the UI using uGUI

        //        if (!_mediaPlayer || _mediaPlayer.Control == null) return;

        //        //GUI.matrix = Matrix4x4.TRS(Vector3.zero, Quaternion.identity, new Vector3(2f, 2f, 1f));

        //        //GUI.backgroundColor = Color.red;
        //        //GUILayout.BeginVertical(GUI.skin.box);
        //        //GUI.backgroundColor = Color.white;

        //        //GUILayout.Label("Duration " + _mediaPlayer.Info.GetDuration() + "s");
        //        //GUILayout.BeginHorizontal();
        //        //GUILayout.Label("States: ");
        //        //GUILayout.Toggle(_mediaPlayer.Control.HasMetaData(), "HasMetaData", GUI.skin.button);
        //        //GUILayout.Toggle(_mediaPlayer.Control.IsPaused(), "Paused", GUI.skin.button);
        //        //GUILayout.Toggle(_mediaPlayer.Control.IsPlaying(), "Playing", GUI.skin.button);
        //        //GUILayout.Toggle(_mediaPlayer.Control.IsBuffering(), "Buffering", GUI.skin.button);
        //        //GUILayout.Toggle(_mediaPlayer.Control.IsSeeking(), "Seeking", GUI.skin.button);
        //        //GUILayout.Toggle(_mediaPlayer.Control.IsFinished(), "Finished", GUI.skin.button);
        //        GUILayout.EndHorizontal();

        //        {
        //            TimeRanges times = _mediaPlayer.Control.GetBufferedTimes();
        //            if (times != null)
        //            {
        //                GUILayout.Label("Buffered Range " + times.MinTime + " - " + times.MaxTime);
        //            }
        //        }
        //        {
        //            TimeRanges times = _mediaPlayer.Control.GetSeekableTimes();
        //            if (times != null)
        //            {
        //                GUILayout.Label("Seek Range " + times.MinTime + " - " + times.MaxTime);
        //            }
        //        }


        //        {
        //            GUILayout.Label("Video Tracks: " + _mediaPlayer.VideoTracks.GetVideoTracks().Count);

        //            GUILayout.BeginVertical();

        //            VideoTrack selectedTrack = null;
        //            foreach (VideoTrack track in _mediaPlayer.VideoTracks.GetVideoTracks())
        //            {
        //                bool isSelected = (track == _mediaPlayer.VideoTracks.GetActiveVideoTrack());
        //                if (isSelected) GUI.color = Color.green;
        //                if (GUILayout.Button(track.DisplayName, GUILayout.ExpandWidth(false)))
        //                {
        //                    selectedTrack = track;
        //                }
        //                if (isSelected) GUI.color = Color.white;
        //            }
        //            GUILayout.EndHorizontal();
        //            if (selectedTrack != null)
        //            {
        //                _mediaPlayer.VideoTracks.SetActiveVideoTrack(selectedTrack);
        //            }
        //        }
        //        {
        //            GUILayout.Label("Audio Tracks: " + _mediaPlayer.AudioTracks.GetAudioTracks().Count);

        //            GUILayout.BeginVertical();

        //            AudioTrack selectedTrack = null;
        //            foreach (AudioTrack track in _mediaPlayer.AudioTracks.GetAudioTracks())
        //            {
        //                bool isSelected = (track == _mediaPlayer.AudioTracks.GetActiveAudioTrack());
        //                if (isSelected) GUI.color = Color.green;
        //                if (GUILayout.Button(track.DisplayName, GUILayout.ExpandWidth(false)))
        //                {
        //                    selectedTrack = track;
        //                }
        //                if (isSelected) GUI.color = Color.white;
        //            }
        //            GUILayout.EndHorizontal();
        //            if (selectedTrack != null)
        //            {
        //                _mediaPlayer.AudioTracks.SetActiveAudioTrack(selectedTrack);
        //            }
        //        }
        //        {
        //            GUILayout.Label("Text Tracks: " + _mediaPlayer.TextTracks.GetTextTracks().Count);

        //            GUILayout.BeginVertical();

        //            TextTrack selectedTrack = null;
        //            foreach (TextTrack track in _mediaPlayer.TextTracks.GetTextTracks())
        //            {
        //                bool isSelected = (track == _mediaPlayer.TextTracks.GetActiveTextTrack());
        //                if (isSelected) GUI.color = Color.green;
        //                if (GUILayout.Button(track.DisplayName, GUILayout.ExpandWidth(false)))
        //                {
        //                    selectedTrack = track;
        //                }
        //                if (isSelected) GUI.color = Color.white;
        //            }
        //            GUILayout.EndHorizontal();
        //            if (selectedTrack != null)
        //            {
        //                _mediaPlayer.TextTracks.SetActiveTextTrack(selectedTrack);
        //            }
        //        }
        //        {
        //            GUILayout.Label("FPS: " + _mediaPlayer.Info.GetVideoDisplayRate().ToString("F2"));
        //        }
        //#if (UNITY_STANDALONE_WIN)
        //			if (_mediaPlayer.PlatformOptionsWindows.bufferedFrameSelection != BufferedFrameSelectionMode.None)
        //			{
        //				IBufferedDisplay bufferedDisplay = _mediaPlayer.BufferedDisplay;
        //				if (bufferedDisplay != null)
        //				{
        //					BufferedFramesState state = bufferedDisplay.GetBufferedFramesState();
        //					GUILayout.BeginHorizontal();
        //					GUILayout.Label("Buffered Frames: " + state.bufferedFrameCount);
        //					GUILayout.HorizontalSlider(state.bufferedFrameCount, 0f, 12f);
        //					GUILayout.EndHorizontal();
        //					GUILayout.BeginHorizontal();
        //					GUILayout.Label("Free Frames: " + state.freeFrameCount);
        //					GUILayout.HorizontalSlider(state.freeFrameCount, 0f, 12f);
        //					GUILayout.EndHorizontal();
        //					GUILayout.Label("Min Timstamp: " + state.minTimeStamp);
        //					GUILayout.Label("Max Timstamp: " + state.maxTimeStamp);
        //					GUILayout.Label("Display Timstamp: " + _mediaPlayer.TextureProducer.GetTextureTimeStamp());
        //				}
        //			}
        //#endif
        //        GUILayout.EndVertical();
        //    }
        #endregion
    }
}

调用

     void Start()
        {
            MediaManager.m_Ins.SetMediaUrl("AVProVideoSamples/BigBuckBunny-360p30-H264.mp4",MediaPathType.RelativeToStreamingAssetsFolder);
            MediaManager.m_Ins.GetMediaPlayer.Events.AddListener(OnVideoEvent);
            //播放
         //   MediaManager.m_Ins.Play();
            //暂停
           // MediaManager.m_Ins.Pause();
           
        }

        private void OnVideoEvent(MediaPlayer arg0, MediaPlayerEvent.EventType arg1, ErrorCode arg2)
        {
            switch (arg1)
            {
                case MediaPlayerEvent.EventType.MetaDataReady:
                    break;
                case MediaPlayerEvent.EventType.ReadyToPlay: 
                    break;
                case MediaPlayerEvent.EventType.Started: 
                    break;
                case MediaPlayerEvent.EventType.FirstFrameReady:
                    break;
                case MediaPlayerEvent.EventType.FinishedPlaying:
                    break;
                case MediaPlayerEvent.EventType.Closing:
                    print(".Closing...");
                    break;
                case MediaPlayerEvent.EventType.Error:
                    print(".Error...");
                    break;
                case MediaPlayerEvent.EventType.SubtitleChange:
                    break;
                case MediaPlayerEvent.EventType.Stalled:
                    break;
                case MediaPlayerEvent.EventType.Unstalled:
                    break;
                case MediaPlayerEvent.EventType.ResolutionChanged:
                    break;
                case MediaPlayerEvent.EventType.StartedSeeking:
                    break;
                case MediaPlayerEvent.EventType.FinishedSeeking:
                    print("播放结束...");
                    break;
                case MediaPlayerEvent.EventType.StartedBuffering:
                    break;
                case MediaPlayerEvent.EventType.FinishedBuffering: 
                    break;
                case MediaPlayerEvent.EventType.PropertiesChanged:
                    break;
                case MediaPlayerEvent.EventType.PlaylistItemChanged:
                    break;
                case MediaPlayerEvent.EventType.PlaylistFinished:
                    break;
                case MediaPlayerEvent.EventType.TextTracksChanged:
                    break;
                //case MediaPlayerEvent.EventType.TextCueChanged://=>  SubtitleChange
                //  break;
                default:
                    break;
            }
        }

        // Update is called once per frame
        void Update()
        {
            if (Input.GetKeyDown(KeyCode.Space))
            {
                 MediaManager.m_Ins.SetMediaUrl("AVProVideoSamples/RenderHeads-1080p30-H264.mp4", MediaPathType.RelativeToStreamingAssetsFolder);
            }
            if (Input.GetKeyDown(KeyCode.A))
            {
                MediaManager.m_Ins.Stop(); 
            }
            if (Input.GetKeyDown(KeyCode.S))
            {
                MediaManager.m_Ins.Rewind(); 
            }
        }

结语

相对Unity自带的VideoPlayer,我更喜欢用这个,功能更齐全,主要是好使,某些平台更好使。
注意这个收费插件在安卓下使用需要在PlayerSettings => OtherSettings =>Rendering =>AutoGraphicsAPI需要取消掉,然后GraphicsAPIS设置OpenGL2或者3.
avpro unity,Unity,unity,java,游戏引擎
源码地址:文章来源地址https://www.toymoban.com/news/detail-697823.html

到了这里,关于Unity播放器插件-AVPro Video - Ultra Edition介绍的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Unity中使用AVPro Video播放视频的完全指南

    Unity AVPro是一款强大的视频播放解决方案,它支持多种视频格式和编解码器,能够在Unity中实现高质量的视频播放。本教程将指导你如何在Unity项目中使用AVPro播放视频。 1、安装AVPro Video 2、在UI上显示视频 3、在3D物体上显示视频,介绍两种播放到物体的方法 4、编写脚本,用按

    2024年04月16日
    浏览(78)
  • springboot + vue3实现视频播放Demo(video.js & Vue3-video-play视频播放器)

    ffmpeg官网 长时长视频java存储及vue播放解决方法 【 攻城略地 】vue3 + video.js播放m3u8视频流格式 Vue3-video-play组件官网 Vue3视频播放器组件Vue3-video-play入门教程 vue-video-player播放m3u8格式的视频 Spring boot视频播放(解决MP4大文件无法播放),整合ffmpeg,用m3u8切片播放。 Java获取MP4视频文

    2024年02月07日
    浏览(75)
  • 【VideoJs】初识videojs && video.js 视频播放器的基本使用 && videojs基础用法 && videojs视频播放器 && vue3中使用videojs

    免费,开源 插件多 可自定义 【推】 虽然,但是Videojs算好了,但我觉得有点杂,特别是文档与插件,且自定义插件有点困难,也可能是我比较菜吧 相比之下,我还是强烈推荐 【Xgplayer ——点我进入】 备用地址 http://t.csdn.cn/H0cAV Xgplayer 优点 优雅、美观 文档清晰明了 大厂出

    2024年02月03日
    浏览(63)
  • vue自定义h5video视频播放器进度条组件,可拖拽、跳转、倍速、全屏

    一个进度条组件控制多个视频的播放、进度调整。视频可点击全屏观看,唯一的进度条是某个指定视频的视频信息。 全屏 点击进度条跳转 拖动滑块 在菜鸟教程上有以下几个参数的详细解说,这张图忘记哪里看的了,如有认领可评论我贴链接 倍速 // 倍速 handleChangeSpeed(item)

    2024年02月12日
    浏览(120)
  • 【Unity】代码控制视频的播放(视频播放器-更新)

     结果如上图,之前写过一个使用代码控制视频播放器的Demo,但是好多小伙伴说我附带的链接没法下载,这次我直接做一个完整版的,不用下载,照着一步一步做就能做出来。 之前写了如何设置RawImage进行自动播放,大家可以看一下基础操作这篇文章:,大佬勿怪。 【Unity】

    2024年02月09日
    浏览(59)
  • 【记录】优化油猴插件【BD网盘播放器】

    有钱的可以支持一下原作者,没钱就要多学习 1.1 视频刚开始未播放时,可能会有弹窗,不需要管,开始播放的时候自动关闭; 1.2 视频播放时,自动关闭弹窗,并继续播放视频 1.3 视频暂停时,每10s检测一次弹窗,自动关闭弹窗 2.1 添加快捷速度调整按钮 2.2 添加2.5和3倍速

    2024年02月16日
    浏览(39)
  • Unity中两种播放器

    一、Animator动画播放器 1.简单搭建UI,通过Slider来控制Cube当前状态动画的播放 2.给Cube加上Animator组件,做两个简单的动画(随便做个来回移动的动画就行),注意要关闭动画的循环播放  3.给摄像机挂在脚本,并拖拽对应组件  4.给Slider挂载脚本 5.运行Unity即可 二、Video Player视

    2024年02月07日
    浏览(41)
  • 基于WebAssembly无插件解码H264/H265码流播放器

    基于WebAssembly无插件解码H264/H265码流播放器 之前看到一篇文章:web无插件解码播放H264/H265(WebAssembly解码HTML5播放) H.265/HEVC在Web视频播放的实践 按照文章思路,已经复现了web端无插件解码H265码流。首先说明下我这边的环境,框架。 在海思主板上移植了web服务器nginx,用于pc端请

    2024年01月16日
    浏览(47)
  • uniapp(全端兼容) - 最新详细禁用video视频组件的拖动快进快退功能,禁止视频播放组件video拖曳改变视频进度,uni-app如何不让视频播放器手动快进和快退(可保证用户完整观看完视频)

    在uniapp小程序/h5网页网站/安卓苹果app/nvue等(全平台完美兼容)开发中,详细实现Video视频组件禁止用户快进和快退,屏蔽禁用屏幕拖拽进行快退和快进功能(禁止用户拖动视频进度条),强制让用户从0-100完整观看完视频允许中途快进,适用于uni-app官方video视频组件,也支持

    2024年04月27日
    浏览(69)
  • 移动端高性能Unity播放器实现方案

    前情提要: 视听体验再进化——如何在24小时内全面升级你的视频应用 如何打造新时代的终端播放产品? 随着VR、AR、元宇宙等新玩法的出现,Unity平台的视频播放需求逐渐增加,比如下面两个动图就是在百度真实的案例。前者是演唱会场景,后者则是一个演讲会场。 通过这

    2024年02月08日
    浏览(43)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包