我有一个Xamarin.Forms应用程序,它有一个视频播放器,我需要在两个平台上为音频播放器实现锁屏播放器控件(用户可以在屏幕锁定时收听音频,也可以在锁屏时播放/暂停)。
我已经实现了这是Xamarin.ios
var audioSession = AVAudioSession.SharedInstance();
audioSession.SetCategory(AVAudioSessionCategory.Playback);
audioSession.SetActive(true);
我在android中实现这个有点困难。我是初级的,需要一点跳跃式的开始。
感谢您抽出时间,非常感谢。祝您愉快!
下面是我的视频播放器渲染器
public class VideoPlayerRenderer : ViewRenderer<VideoPlayer, ARelativeLayout>
{
public static VideoView VideoView;
private MediaController mediaController; // Used to display transport controls
private bool IsPrepared;
private readonly Context Context;
private Android.Widget.ProgressBar ProgressBar;
private bool HasScrollView = false;
private ImageView ImageView;
private ARelativeLayout.LayoutParams LayoutParam;
public event EventHandler<bool> FullScreenStatusChanged;
public static DeviceTimer deviceTimer;
private string uri;
private readonly TVVideoTVService TVVideoTvLoggingUpdate = new TVVideoTVService();
public static bool IsUserclickedFullscreen;
public static int SeekTimeBack = 0;
public static bool IsUserSeekedPrgressedBar = true;
public TVVideoTVLoggingRequest TVVideoTVLoggingRequest;
public System.Threading.Timer timer;
public VideoPlayerRenderer(Context context) : base(context)
{
Context = context;
MessagingCenter.Subscribe<string>("TVVideoTV", "StopAndoridTimer", (sender) =>
{
if (deviceTimer != null)
deviceTimer.Stop();
});
}
private void InitProgressBar()
{
try
{
ProgressBar = new Android.Widget.ProgressBar(Context)
{
Indeterminate = false,
Visibility = Android.Views.ViewStates.Invisible
};
var lparams = new ARelativeLayout.LayoutParams(5, 5);
lparams.AddRule(LayoutRules.EndOf);
Control.AddView(ProgressBar, lparams);
}
catch (Exception ex)
{
var properties = new Dictionary<string, string>
{
{ "Location", "Method - InitProgressBar , class - VideoPlayerRenderer" },
{ "Error", ex.ToString() },
};
Crashes.TrackError(new Exception(), properties);
throw;
}
}
protected override void OnElementChanged(ElementChangedEventArgs<VideoPlayer> args)
{
var extraInfo = new TVVideoTVLoggingRequest();
try
{
base.OnElementChanged(args);
if (args.NewElement != null)
{
if (Control == null)
{
// Save the VideoView for future reference
VideoView = new VideoView(Context);
// Put the VideoView in a RelativeLayout
var relativeLayout = new ARelativeLayout(Context);
relativeLayout.AddView(VideoView);
// Center the VideoView in the RelativeLayout
LayoutParam = new ARelativeLayout.LayoutParams(LayoutParams.MatchParent, LayoutParams.MatchParent);
LayoutParam.AddRule(LayoutRules.CenterInParent);
VideoView.LayoutParameters = LayoutParam;
// Handle a VideoView event
VideoView.Prepared += OnVideoViewPrepared;
SetNativeControl(relativeLayout);
}
//initialize the Uri to video player
SetAreTransportControlsEnabled();
InitProgressBar();
SetSource();
args.NewElement.UpdateStatus += OnUpdateStatus;
args.NewElement.PlayRequested += OnPlayRequested;
args.NewElement.PauseRequested += OnPauseRequested;
args.NewElement.StopRequested += OnStopRequested;
}
if (args.OldElement != null)
{
args.OldElement.UpdateStatus -= OnUpdateStatus;
args.OldElement.PlayRequested -= OnPlayRequested;
args.OldElement.PauseRequested -= OnPauseRequested;
args.OldElement.StopRequested -= OnStopRequested;
}
// seek event for android
VideoView.SetOnPreparedListener(new PreparedListener(mediaController));
//Loging end of video
VideoView.Completion += async delegate
{
deviceTimer.Stop();
extraInfo = await LoggingTVVideoTVActivity((int)CPDLoggingType.VideoEnd);
};
//Loging Errors of video
VideoView.Error += async (Error_obj, Error_args) =>
{
var extraInfo = await LoggingTVVideoTVActivity((int)CPDLoggingType.VideoError, Newtonsoft.Json.JsonConvert.SerializeObject(Error_args));
var properties = new Dictionary<string, string>
{
{ "Location", "Method - OnElementChanged , Class - VideoPlayerRender" },
{ "MoreInfo",Newtonsoft.Json.JsonConvert.SerializeObject(extraInfo)}
};
Crashes.TrackError(new Exception(), properties);
};
InitializeFullScreenCapability();
}
catch (Exception ex)
{
var properties = new Dictionary<string, string>
{
{ "Location", "Method - OnPlayRequested , class - VideoPlayerRenderer" },
{ "Error", ex.ToString() },
{ "MoreInfo",Newtonsoft.Json.JsonConvert.SerializeObject(extraInfo)}
};
Crashes.TrackError(new Exception(), properties);
throw;
}
}
private void OnPauseRequested(object sender, EventArgs e)
{
throw new NotImplementedException();
}
/// <summary>
/// initalize the fullscreen
/// </summary>
private void InitializeFullScreenCapability()
{
try
{
Xamarin.Forms.Element view = Element;
while (view.Parent != null)
{
view = view.Parent;
if (view is Xamarin.Forms.ScrollView)
{
HasScrollView = true;
break;
}
}
//initialize fullscreen button
ImageView = new ImageView(Context) { };
ImageView.SetImageResource(App.Droid.Resource.Drawable.fullscreen);
ImageView.SetPadding(0, 30, 30, 0);
var lv = new ARelativeLayout.LayoutParams(80, 80);
lv.AddRule(LayoutRules.AlignParentRight);
ImageView.LayoutParameters = lv;
Control.AddView(ImageView);
//creating the click event for full screen
ImageView.Click += delegate
{
FullScreen(HasScrollView);
};
}
catch (Exception ex)
{
var properties = new Dictionary<string, string>
{
{ "Location", "Method - InitializeFullScreenCapability , class - VideoPlayerRenderer" },
{ "Error", ex.ToString() },
};
Crashes.TrackError(new Exception(), properties);
throw;
}
}
/// <summary>
/// move to full screen view
/// </summary>
/// <param name="resizeLayout"></param>
private void FullScreen(bool resizeLayout = false)
{
try
{
LoggingTVVideoTVActivity((int)CPDLoggingType.VideoFullscreen);
VideoView.Pause();
deviceTimer.Stop();
IsUserclickedFullscreen = true;
var intent = new Intent(MainActivity.Instance, typeof(FullScreen));
intent.PutExtra("uri", uri);
intent.PutExtra("seek", VideoView.CurrentPosition);
MainActivity.Instance.StartActivity(intent);
}
catch (Exception ex)
{
var properties = new Dictionary<string, string>
{
{ "Location", "Method - FullScreen , class - VideoPlayerRenderer" },
{ "Error", ex.ToString() },
};
Crashes.TrackError(new Exception(), properties);
throw;
}
}
// this mathod Handle that wraps your unmanaged resource
protected override void Dispose(bool disposing)
{
if (Control != null && VideoView != null)
{
VideoView.Prepared -= OnVideoViewPrepared;
}
if (Element != null)
{
Element.UpdateStatus -= OnUpdateStatus;
}
base.Dispose(disposing);
}
/// <summary>
/// seekbar setup for video
/// </summary>
/// <param name="sender"></param>
/// <param name="args"></param>
public void OnVideoViewPrepared(object sender, EventArgs args)
{
try
{
ProgressBar.Visibility = Android.Views.ViewStates.Invisible;
IsPrepared = true;
if (SeekTimeBack != 0)
{
IsUserSeekedPrgressedBar = false;
VideoView.SeekTo(SeekTimeBack);
VideoView.Start();
}
var seekbarId = mediaController.Resources.GetIdentifier("mediacontroller_progress", "id", "android");
var seekBar = mediaController.FindViewById<SeekBar>(seekbarId);
((IVideoPlayerController)Element).Duration = TimeSpan.FromMilliseconds(VideoView.Duration);
}
catch (Exception ex)
{
var properties = new Dictionary<string, string>
{
{ "Location", "Method - OnVideoViewPrepared , class - VideoPlayerRenderer" },
{ "Error", ex.ToString() },
};
Crashes.TrackError(new Exception(), properties);
throw;
}
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs args)
{
try
{
base.OnElementPropertyChanged(sender, args);
if (args.PropertyName == VideoPlayer.AreTransportControlsEnabledProperty.PropertyName)
{
SetAreTransportControlsEnabled();
}
else if (args.PropertyName == VideoPlayer.SourceProperty.PropertyName)
{
SetSource();
}
else if (args.PropertyName == VideoPlayer.PositionProperty.PropertyName)
{
//this mathod move the seek button over the seek bar
if (Math.Abs(VideoView.CurrentPosition - Element.Position.TotalMilliseconds) > 1000)
{
VideoView.SeekTo((int)Element.Position.TotalMilliseconds);
}
}
}
catch (Exception ex)
{
var properties = new Dictionary<string, string>
{
{ "Location", "Method - OnElementPropertyChanged , class - VideoPlayerRenderer" },
{ "Error", ex.ToString() },
};
Crashes.TrackError(new Exception(), properties);
throw;
}
}
/// <summary>
/// initialize the all controllers example playbutton and progres bar
/// </summary>
public void SetAreTransportControlsEnabled()
{
try
{
if (Element.AreTransportControlsEnabled)
{
mediaController = new MediaController(Context);
mediaController.SetMediaPlayer(VideoView);
VideoView.SetMediaController(mediaController);
}
else
{
VideoView.SetMediaController(null);
if (mediaController != null)
{
mediaController.SetMediaPlayer(null);
mediaController = null;
}
}
}
catch (Exception ex)
{
var properties = new Dictionary<string, string>
{
{ "Location", "Method - SetAreTransportControlsEnabled , class - VideoPlayerRenderer" },
{ "Error", ex.ToString() },
};
Crashes.TrackError(new Exception(), properties);
throw;
}
}
/// <summary>
/// bindig source for TVVideoTV
/// </summary>
public void SetSource()
{
try
{
IsPrepared = false;
var hasSetSource = false;
// currenty we use this ---
if (Element.Source is UriVideoSource)
{
uri = (Element.Source as UriVideoSource).Uri;
if (!string.IsNullOrWhiteSpace(uri))
{
VideoView.SetVideoURI(Android.Net.Uri.Parse(uri));
hasSetSource = true;
}
}
else if (Element.Source is FileVideoSource)
{
var filename = (Element.Source as FileVideoSource).File;
if (!string.IsNullOrWhiteSpace(filename))
{
VideoView.SetVideoPath(filename);
hasSetSource = true;
}
}
if (hasSetSource && Element.AutoPlay)
{
VideoView.Start();
}
}
catch (Exception ex)
{
var properties = new Dictionary<string, string>
{
{ "Location", "Method - SetSource , class - VideoPlayerRenderer" },
{ "Error", ex.ToString() },
};
Crashes.TrackError(new Exception(), properties);
throw;
}
}
/// <summary>
/// Event handler to update status
/// </summary>
/// <param name="sender"></param>
/// <param name="args"></param>
public void OnUpdateStatus(object sender, EventArgs args)
{
try
{
var status = VideoStatus.NotReady;
if (IsPrepared)
{
status = VideoView.IsPlaying ? VideoStatus.Playing : VideoStatus.Paused;
}
((IVideoPlayerController)Element).Status = status;
// Set Position property
var timeSpan = TimeSpan.FromMilliseconds(VideoView.CurrentPosition);
((IElementController)Element).SetValueFromRenderer(VideoPlayer.PositionProperty, timeSpan);
}
catch (Exception ex)
{
var properties = new Dictionary<string, string>
{
{ "Location", "Method - OnUpdateStatus , class - VideoPlayerRenderer" },
{ "Error", ex.ToString() },
};
Crashes.TrackError(new Exception(), properties);
throw;
}
}
/// <summary>
/// logging the play and pause activities
/// </summary>
/// <param name="sender"></param>
/// <param name="args"></param>
public async void OnPlayRequested(object sender, EventArgs args)
{
var extraInfo = new TVVideoTVLoggingRequest();
try
{
if (VideoView.IsPlaying)
{
VideoView.Pause();
extraInfo = await LoggingTVVideoTVActivity((int)CPDLoggingType.VideoPause);
deviceTimer.Stop();
}
else
{
VideoView.Start();
deviceTimer = new DeviceTimer(TimeSpan.FromSeconds(300), () => LoggingTVVideoTVActivity((int)CPDLoggingType.VideoPlay));
deviceTimer.Start();
extraInfo = await LoggingTVVideoTVActivity((int)CPDLoggingType.VideoPlay);
}
}
catch (Exception ex)
{
var properties = new Dictionary<string, string>
{
{ "Location", "Method - OnPlayRequested , class - VideoPlayerRenderer" },
{ "Error", ex.ToString() },
{ "MoreInfo",Newtonsoft.Json.JsonConvert.SerializeObject(extraInfo)}
};
Crashes.TrackError(new Exception(), properties);
throw;
}
}
/// <summary>
/// detect onstop and loging
/// </summary>
/// <param name="sender"></param>
/// <param name="args"></param>
public async void OnStopRequested(object sender, EventArgs args)
{
var extraInfo = new TVVideoTVLoggingRequest();
try
{
VideoView.StopPlayback();
extraInfo = await LoggingTVVideoTVActivity((int)CPDLoggingType.VideoStop);
}
catch (Exception ex)
{
var properties = new Dictionary<string, string>
{
{ "Location", "Method - OnStopRequested , class - VideoPlayerRenderer" },
{ "Error", ex.ToString() },
{ "MoreInfo",Newtonsoft.Json.JsonConvert.SerializeObject(extraInfo)}
};
Crashes.TrackError(new Exception(), properties);
throw;
}
}
/// <summary>
/// Logging TVVideoTV activity in by loggingTypeID
/// </summary>
/// <param name="loggingTypeID"></param>
public async Task<TVVideoTVLoggingRequest> LoggingTVVideoTVActivity(int loggingTypeID, string extraInfo = "")
{
var itemTime = VideoView.CurrentPosition;
//Prevents CPD video play logging current position as 0.0 in full screen view.
//This check "itemTime == 0.0" added when video is in full screen view, normal video view is returning current position as 0.0
if (itemTime == 0.0 && CustomVideoView.mListener != null)
{
itemTime = CustomVideoView.mListener.CurrentPosition; //Helps to get the current position from Full screen video view.
}
var request = new TVVideoTVLoggingRequest()
{
CPDLoggingTypeID = loggingTypeID,
ItemID = TVVideoTVModalViewModel.TVVideoTV.EventID,
ItemTitle = TVVideoTVModalViewModel.TVVideoTV.Title,
ItemTime = itemTime * 0.001f, //convert time to seconds from ms
Extra = "Video ID -> " + TVVideoTVModalViewModel.TVVideoTV.VideoPlayerCode + " Extra info -> " + extraInfo
};
_ = await TVVideoTvLoggingUpdate.TVVideoTVLoggingUpdate(request);
return request; // only returns for send logs to app center
}
}
感谢您抽出时间,非常感谢。祝您愉快!
1条答案
按热度按时间hivapdat1#
你可以检查这个link。这是一个由Xamarin官方编写的MediaManager插件。
另外,你也可以引用this,这是在Xamarin Forms内调用
Locker.Activate()
的一种方法,就是使用Dependency Service
。