I created a simple test UWP app with one MainPage, that has MediaPlayer:
public sealed partial class MainPage
{
public MainPage()
{
InitializeComponent();
Loaded += MainPage_Loaded;
}
private void MainPage_Loaded(object sender, RoutedEventArgs e)
{
var mediaPlayer = new MediaPlayer
{
Source = MediaSource.CreateFromUri(new Uri("ms-appx:///Assets/preview.mp4")),
AutoPlay = true
};
mediaPlayer.AddVideoEffect(typeof(VideoEffect).FullName, true, null);
}
}
and WinRT component with IBasicVideoEffect inherited class that notifies me how many frames were processed:
public sealed class VideoEffect : IBasicVideoEffect
{
public IReadOnlyList<VideoEncodingProperties> SupportedEncodingProperties => new List<VideoEncodingProperties>();
public bool IsReadOnly => false;
public MediaMemoryTypes SupportedMemoryTypes => MediaMemoryTypes.Gpu;
public void SetProperties(IPropertySet configuration) { }
public bool TimeIndependent => false;
public void Close(MediaEffectClosedReason reason) { }
public void DiscardQueuedFrames() { }
private int _frameCounter;
public void ProcessFrame(ProcessVideoFrameContext context)
{
_frameCounter++;
Debug.WriteLine("Frame #" + _frameCounter);
}
public void SetEncodingProperties(VideoEncodingProperties encodingProperties, IDirect3DDevice device)
{
Debug.WriteLine("SetEncodingProperties");
}
}
If I run it - only 3 frames will be processed no matter what video file will be.
If I set breakpoint where _frameCounter increments I'll manage to hit F5 for 8 frames.
Why and how can I get all the frames to be processed?
I can solve it using MediaClip and MediaComposition as many examples say, but in this case frames are processed by CPU not GPU video engine which is not my goal.
The problem here is that you are playing a MediaPlayer without displaying it in XAML. So only the first few frames was processed as the media is not rendering on UI, there is no need to process other frames.
To make your VideoEffect work, you can use MediaPlayerElement control with MediaPlayerElement.SetMediaPlayer method to to render the media.
XAML:
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<MediaPlayerElement x:Name="mediaPlayerElement" />
</Grid>
Code-behind:
private void MainPage_Loaded(object sender, RoutedEventArgs e)
{
var mediaPlayer = new MediaPlayer
{
Source = MediaSource.CreateFromUri(new Uri("ms-appx:///Assets/preview.mp4")),
AutoPlay = true
};
mediaPlayer.AddVideoEffect(typeof(VideoEffect).FullName, true, null);
mediaPlayerElement.SetMediaPlayer(mediaPlayer);
}
After this, you will see _frameCounter increases while the media is playing.
with MediaPlayer you have to add effect before set source.
effect.AddVideoEffect(_player);
_player.Source = MediaSource.CreateFromMediaStreamSource(source);
Related
I have a MediaPlayerElement and the following class to control it:
class MediaPlayer : INotifyPropertyChanged
{
private MediaPlaybackState _State;
public MediaPlaybackState State
{
get
{
return _State;
}
set
{
_State = value;
OnPropertyChanged();
}
}
public BitmapImage AlbumArt;
public string Title;
public string Subtitle;
private MediaPlayerElement mediaPlayer;
public event PropertyChangedEventHandler PropertyChanged = delegate { };
public void PlayerStateChanged(MediaPlaybackSession session, object sender)
{
State = session.PlaybackState;
}
public void SetMediaElement(MediaPlayerElement param)
{
mediaPlayer = param;
}
public void PlayFromSearchResult(SearchResult result)
{
AlbumArt = new BitmapImage();
AlbumArt.UriSource = new Uri(result.StationImage);
Title = result.StationName;
Subtitle = result.Subtext;
PlayFromRemoteM3U(result.StreamURL);
}
public void Pause()
{
mediaPlayer.MediaPlayer.Pause();
}
public void Play()
{
mediaPlayer.MediaPlayer.Play();
mediaPlayer.MediaPlayer.PlaybackSession.PlaybackStateChanged += PlayerStateChanged;
}
public async void PlayFromRemoteM3U(string url)
{
--SNIP--
mediaPlayer.Source = MediaSource.CreateFromUri(new Uri(streamDownloadUrl));
Play();
}
}
protected void OnPropertyChanged([CallerMemberName] string name = null)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
}
I would like to have a play/pause button that changes it's content based on the current player state, I'm currently debugging with a TextBlock to display the current state:
<TextBlock x:Name="media_player_state" x:FieldModifier="public" FontSize="20" Text="{x:Bind MediaPlayerInstance.State, Mode=OneWay}">Play</TextBlock>
When I run the application and start a stream so the state changes, I get the following error:
The application called an interface that was marshalled for a different thread. (Exception from HRESULT: 0x8001010E (RPC_E_WRONG_THREAD))
I want to know if there I am trying to accomplish this the right way or how to fix this.
Thanks
Because you're calling it from another thread, you need to use a dispatcher
public void PlayerStateChanged(MediaPlaybackSession session, object sender)
{
DispatcherHelper.ExecuteOnUIThreadAsync(() =>
{
State = session.PlaybackState;
});
}
please note 2 things:
1- DispatcherHelper is part of the windows community toolkit nuget package (https://www.nuget.org/profiles/Microsoft.Toolkit)
2- DispatcherHelper will be deprecated in favor of the better DispatcherQueueHelper in the 7.0 release
Because you are not setting the state everytime you Play() or Pause().
Change the _State value and then use OnpropertyChanged("State")
I have to rotate between URLs(lets say 10 urls). Every url has its own Webview and each webview is shown for 15 secs(one at a time). I can change the urls from the server and that immediately shows onto the UWP application.
If the internet is out, the WebViews should still rotate between all the urls after the interval that is why we are using multiple webviews.
Currently, the situation is, that the more URLs I change, the more RAM it takes and eventually hangs.
WebViews is heavyweight control, please don't create multiple instance to render html page, for the scenario, you could use timer to change one webview's source with mvvm pattern for each 15s. Even if the internet is out, it will still work. Please check the following code.
public sealed partial class MainPage : Page, INotifyPropertyChanged
{
public MainPage()
{
this.InitializeComponent();
initUri();
var timer = new DispatcherTimer();
timer.Interval = TimeSpan.FromSeconds(15);
timer.Tick += Timer_Tick;
timer.Start();
Source = new Uri("xxxxxx");
}
private List<Uri> _uris = new List<Uri>();
private void initUri()
{
_uris.Add(new Uri("xxxxxx"));
_uris.Add(new Uri("xxxxxx"));
_uris.Add(new Uri("xxxxxx"));
}
int count = 0;
private void Timer_Tick(object sender, object e)
{
Source = _uris[count];
count++;
if (count == _uris.Count)
{
count = 0;
}
}
public event PropertyChangedEventHandler PropertyChanged;
private void OnPropertyChanged([CallerMemberName] string propertyName = null)
{
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
private Uri _source;
public Uri Source
{
get
{
return _source;
}
set
{
_source = value;
OnPropertyChanged();
}
}
}
Xaml
<WebView
x:Name="MyWebView"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
Source="{x:Bind Source, Mode=OneWay}"
/>
i'm trying to create a Popup page (using Rg.Plugin.Popup) that has to display a stream video got from an API. Everytime i navigate into this page, i want to display a different video, obtained from the API, based on parameters provided by the page I come from (i get them by a message sent by Messenger). It seems to work at first run but, when i close the Popup page, and i open it again the videoView is all black and in the output is written: Failed to get window format.
That's my code ( base on sample provided here: https://code.videolan.org/videolan/LibVLCSharp/tree/master/Samples/Forms):
The code-behind page:
public partial class WebcamVideoPopUpPage : PopupPage
{
public WebcamVideoPopUpPage()
{
var vm = App.Locator.WebCamVideoVM;
this.BindingContext = vm;
InitializeComponent();
}
protected override void OnAppearing()
{
base.OnAppearing();
Messenger.Default.Send(new OnApperingVideoMessage());
}
private void VideoView_MediaPlayerChanged(object sender,
LibVLCSharp.Shared.MediaPlayerChangedEventArgs e)
{
Messenger.Default.Send(new OnVideoViewInitializedMessage());
}
protected override void OnDisappearing()
{
base.OnDisappearing();
}
}
videoView in xaml:
<shared:VideoView x:Name="VideoView"
MediaPlayer ="{Binding MediaPlayer}"
HorizontalOptions ="FillAndExpand"
VerticalOptions ="FillAndExpand"
MediaPlayerChanged ="VideoView_MediaPlayerChanged"/>
the ViewModel:
public class WebcamVideoViewModel : BaseViewModel
{
private LibVLC LibVLC { get; set; }
private bool IsLoaded { get; set; }
private bool IsVideoViewInitialized { get; set; }
private Media media { get; set; }
private MediaPlayer _mediaPlayer;
public MediaPlayer MediaPlayer
{
get { return _mediaPlayer; }
set
{
_mediaPlayer = value;
OnPropertyChanged();
}
}
public WebcamVideoViewModel(INavigationService navigationService, IApiManagerFactory apiFactory) : base(navigationService, apiFactory)
{
Messenger.Default.Register<InfoWebcamVideoMessage>(this, OnReceivedInfoWebcam);
Messenger.Default.Register<OnApperingVideoMessage>(this, OnAppearing);
Messenger.Default.Register<OnVideoViewInitializedMessage>(this, OnVideoViewInitialized);
Task.Run(Initialize);
}
private void Initialize()
{
Core.Initialize();
LibVLC = new LibVLC();
MediaPlayer = new MediaPlayer(LibVLC);
}
private async void OnReceivedInfoWebcam(InfoWebcamVideoMessage msg)
{
var response = await ApiManager.GetVideoWebcam(msg.Mpr, msg.Uuid);
if (response.IsSuccessStatusCode)
{
var stream = await response.Content.ReadAsStreamAsync();
media = new Media(LibVLC, stream);
Play();
}
}
public void OnAppearing(OnApperingVideoMessage msg)
{
IsLoaded = true;
}
public void OnVideoViewInitialized(OnVideoViewInitializedMessage msg)
{
IsVideoViewInitialized = true;
}
private void Play()
{
if (IsLoaded && IsVideoViewInitialized)
{
MediaPlayer.Play(media);
}
}
}
I resolved my problem overriding OnAppering method:
protected override void OnDisappearing()
{
base.OnDisappearing();
VideoView.MediaPlayer.Stop();
VideoView.MediaPlayer = null;
}
I am using MediaElement to show video clips in a loop for long period of time. After some time (hours for Win 7 / 4 GB RAM) the program crashes with exception of type "Insufficient memory". I have monitored the memory used while playing with Process Explorer-Sysinternals and also logged it using System.Diagnostics.Process methods. Both ways show gradually increasing of used memory.
Here is the code:
XAML:
<Grid Name="GridTest">
<MediaElement x:Name="MediaPlayer"
LoadedBehavior="Manual"
MediaEnded="VideoControl_MediaEnded"
MediaOpened="MediaPlayer_MediaOpened"
Source="{Binding Mode=OneWay,
Path=MySource}" />
</Grid>
.cs:
public partial class MainWindow : Window
{
public MainViewModel model = new MainViewModel();
public MainWindow()
{
InitializeComponent();
this.GridTest.DataContext = model;
// fill in model.MediaFilesUris:
...
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
// choose the next media file
...
MediaPlayer.Play();
}
private void VideoControl_MediaEnded(object sender, RoutedEventArgs e)
{
// choose the next media file
...
model.OnPropertyChanged("MySource");
MediaPlayer.Play();
}
}
public class MainViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
public void OnPropertyChanged(string propertyName)
{
PropertyChangedEventHandler handler = PropertyChanged;
if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName));
}
public Uri[] MediaFilesUris = null;
public int crn = 0;
public Uri MySource { get { if (MediaFilesUris != null && MediaFilesUris.Count()>0) return MediaFilesUris[crn]; else return null; } }
}
I have also tested the case when MediaElement object is created dynamically, destroyed (together with all unsubscribing from events, etc.) after several clips and created again. Memory got consumed increasingly again.
Any suggestions would be appreciated!
Try to specify MediaElement UnloadingBehavior="Close"property in your XAML.
According to MSDN MediaState::Close indicates that
All media resources are released (including video memory).
My proposal is to make the following:
private void VideoControl_MediaEnded(object sender, RoutedEventArgs e)
{
// choose the next media file
...
//make the following explicitly
MediaPlayer.Stop();
MediaPlayer.Source = null;
model.OnPropertyChanged("MySource");
MediaPlayer.Play();
}
Hello Guys i'm working on a app that when you click a button it opens the stock iOS camera and lets you take a picture I know how to do this in objc but the apps written in C# using Xamarin i've looked on the Xamarin forms and google for help but everything is post iOS 8 witch this app needs to run so here's the code that I have so far:
photobutton.TouchUpInside += delegate {
//insert Xamarin code here
};
EDIT I ADDED THE Following code to a new class:
using Foundation;
using System;
using System.CodeDom.Compiler;
using UIKit;
namespace ToolBelt.iOS
{
public partial class cameraViewController : UIViewController
{
public cameraViewController (IntPtr handle) : base (handle)
{
}
public override void DidReceiveMemoryWarning ()
{
// Releases the view if it doesn't have a superview.
base.DidReceiveMemoryWarning ();
// Release any cached data, images, etc that aren't in use.
}
public override void ViewDidLoad ()
{
base.ViewDidLoad ();
//UIPopoverController popover = new UIPopoverController (ctrl);
// Perform any additional setup after loading the view, typically from a nib.
}
public override void ViewDidAppear (bool animated)
{
base.ViewDidAppear (animated);
UIImagePickerController picker = new UIImagePickerController ();picker.PrefersStatusBarHidden ();
picker.SourceType = UIImagePickerControllerSourceType.Camera;
PresentViewController(picker, true, () => {});
}
}
}
Any Help would be awesome
Thank you in advance!
You've almost finished it. Just add Delegate handler for Picker, take a look on this: https://developer.xamarin.com/recipes/ios/media/video_and_photos/choose_a_photo_from_the_gallery/
I added events below follow your existing source code
UIImagePickerController imagePicker;
public override void ViewDidAppear (bool animated)
{
base.ViewDidAppear (animated);
imagePicker = new UIImagePickerController();
imagePicker.PrefersStatusBarHidden ();
imagePicker.SourceType = UIImagePickerControllerSourceType.Camera;
//Add event handlers when user finished Capturing image or Cancel
imagePicker.FinishedPickingMedia += Handle_FinishedPickingMedia;
imagePicker.Canceled += Handle_Canceled;
//present
PresentViewController(picker, true, () => {});
}
protected void Handle_FinishedPickingMedia (object sender, UIImagePickerMediaPickedEventArgs e)
{
// determine what was selected, video or image
bool isImage = false;
switch(e.Info[UIImagePickerController.MediaType].ToString()) {
case "public.image":
Console.WriteLine("Image selected");
isImage = true;
break;
case "public.video":
Console.WriteLine("Video selected");
break;
}
// get common info (shared between images and video)
NSUrl referenceURL = e.Info[new NSString("UIImagePickerControllerReferenceUrl")] as NSUrl;
if (referenceURL != null)
Console.WriteLine("Url:"+referenceURL.ToString ());
// if it was an image, get the other image info
if(isImage) {
// get the original image
UIImage originalImage = e.Info[UIImagePickerController.OriginalImage] as UIImage;
if(originalImage != null) {
// do something with the image
Console.WriteLine ("got the original image");
imageView.Image = originalImage; // display
}
} else { // if it's a video
// get video url
NSUrl mediaURL = e.Info[UIImagePickerController.MediaURL] as NSUrl;
if(mediaURL != null) {
Console.WriteLine(mediaURL.ToString());
}
}
// dismiss the picker
imagePicker.DismissModalViewControllerAnimated (true);
}
void Handle_Canceled (object sender, EventArgs e) {
imagePicker.DismissModalViewControllerAnimated(true);
}
Im running good with this:
public partial class CameraViewController : UIViewController
{
static UIImagePickerController picker;
static UIImageView staticImageView;
public CameraViewController() : base("CameraViewController", null)
{
}
public override void ViewDidLoad()
{
base.ViewDidLoad();
base.Title = "Kamera";
staticImageView = this.imageView;
}
partial void openCamera(UIButton sender)
{
if (UIImagePickerController.IsSourceTypeAvailable(UIImagePickerControllerSourceType.Camera))
{
picker = new UIImagePickerController();
picker.Delegate = new CameraDelegate();
picker.SourceType = UIImagePickerControllerSourceType.Camera;
NavigationController.PresentViewController(picker, true, null);
}
else
{
this.button.Hidden = true;
}
}
class CameraDelegate : UIImagePickerControllerDelegate
{
public override void FinishedPickingMedia(UIImagePickerController picker, NSDictionary info)
{
picker.DismissModalViewController(true);
var image = info.ValueForKey(new NSString("UIImagePickerControllerOriginalImage")) as UIImage;
CameraViewController.staticImageView.Image = image;
}
}
}
The method "openCamera" is connected to the Button Event via .xib file.
You can use XLabs: https://github.com/XLabs/Xamarin-Forms-Labs
XLabs provides the Camera service which can take a picture: https://github.com/XLabs/Xamarin-Forms-Labs/wiki/Camera
If you are not familiar with XLabs, these are a few links which help you getting started. Please remember to add the XLabs nuget package to all of your projects (PCL, iOS, Android)
http://www.codenutz.com/getting-started-xamarin-forms-labs-xaml-mvvm-ioc/
https://github.com/XLabs/Xamarin-Forms-Labs/tree/master/Samples
How to use Resolver in XLabs: https://forums.xamarin.com/discussion/20178/xamarin-forms-labs-peeps
EDIT: XLabs MediaPicker can be used in both Xamarin.Forms and non-Xamarin.Forms app.