How to use TimedTextSource to view (srt) subtitle on MediaElement - c#

I know that it is possible to view external subtitles with MediaElement but I do not get it that how. Can anybody explain me how to connect subtitles with MediaElement.
This is only clue I know so far. But how to tell MediaElement to use this?
TimedTextSource.CreateFromUri(new Uri("ms-appx:///TheVideoTitle.srt"), "en");
I found lot of information about this from Microsoft website:
https://learn.microsoft.com/en-us/uwp/api/windows.media.core.timedtextsource (Editied www url. By accident I pasted wrong one)
but they do not have any clear examples.
UPDATE
I created the following code. It starts to play video but still subtitle is missing. What's the problem. MediaElement has been created in XAML like this:
<MediaPlayerElement x:Name="MyVideoPlayer" Stretch="Fill"/>
This code still not work:
//Get MediaPlayerElement
MediaPlayerElement VideoPlayer = 'THE MEDIAPLAYER ELEMENT IN XAML';
//Get video file StorageFile
StorageFile VideoStorageFile = await StorageFile.GetFileFromPathAsync(GetMultimediaPlayer(ID).LocalVideoInformation.TitleFilePathOnStorage);
//Create MediaSource using VideoStorageFile as source
MediaSource VideoMediaSource = MediaSource.CreateFromStorageFile(VideoStorageFile);
//Create subtitle Uri
Uri SubtitleUri = new Uri("ms-appx:///TheTestSubtitle.srt");
//Set SubtitleUri as source of TimedTextSource
TimedTextSource SubtitleTimedTextSource = TimedTextSource.CreateFromUri(SubtitleUri);
//Add TimedTextSource (SubtitleTimedTextSource) to MediaSource (VideoMediaSource)
VideoMediaSource.ExternalTimedTextSources.Add(SubtitleTimedTextSource);
//Set MediaPlayerElement (VideoPlayer) source
VideoPlayer.Source = VideoMediaSource;
//Play
VideoPlayer.MediaPlayer.Play();
OK...Tried to copy from example code in GibHub but still the following code not work. In a void Tts-Resolved args.Error occur. (The system cannot find the file specifield.) Wondering what is the problem: All srt files etc. are exactly in same folders than in example app.
public sealed partial class MainPage : Page
{
private Dictionary<TimedTextSource, Uri> ttsMap = new Dictionary<TimedTextSource, Uri>();
public MainPage()
{
this.InitializeComponent();
}
private void Page_Loaded(object sender, RoutedEventArgs e)
{
var source = MediaSource.CreateFromUri(new Uri("https://mediaplatstorage1.blob.core.windows.net/windows-universal-samples-media/elephantsdream-clip-h264_sd-aac_eng-aac_spa-aac_eng_commentary.mp4"));
var ttsEnUri = new Uri("ms-appx:///Assets/Media/ElephantsDream-Clip-SRT_en.srt");
var ttsEn = TimedTextSource.CreateFromUri(ttsEnUri);
ttsMap[ttsEn] = ttsEnUri;
var ttsPtUri = new Uri("ms-appx:///Assets/Media/ElephantsDream-Clip-SRT_pt.srt");
var ttsPt = TimedTextSource.CreateFromUri(ttsPtUri);
ttsMap[ttsPt] = ttsPtUri;
var ttsSvUri = new Uri("ms-appx:///Assets/Media/ElephantsDream-Clip-SRT_sv.srt");
var ttsSv = TimedTextSource.CreateFromUri(ttsSvUri);
ttsMap[ttsSv] = ttsSvUri;
ttsEn.Resolved += Tts_Resolved;
ttsPt.Resolved += Tts_Resolved;
ttsSv.Resolved += Tts_Resolved;
source.ExternalTimedTextSources.Add(ttsEn);
source.ExternalTimedTextSources.Add(ttsPt);
source.ExternalTimedTextSources.Add(ttsSv);
var playbackItem = new MediaPlaybackItem(source);
playbackItem.TimedMetadataTracksChanged += (item, args) =>
{
playbackItem.TimedMetadataTracks.SetPresentationMode(0, TimedMetadataTrackPresentationMode.PlatformPresented);
};
mediaPlayerElement.Source = playbackItem;
}
private void Tts_Resolved(TimedTextSource sender, TimedTextSourceResolveResultEventArgs args)
{
var ttsUri = ttsMap[sender];
if (args.Error != null)
{
return;
}
var ttsUriString = ttsUri.AbsoluteUri;
if (ttsUriString.Contains("_en"))
args.Tracks[0].Label = "English";
else if (ttsUriString.Contains("_pt"))
args.Tracks[0].Label = "Portuguese";
else if (ttsUriString.Contains("_sv"))
args.Tracks[0].Label = "Swedish";
}
}
}

You have to add the timed text source to ExternalTimedTextSources collection of the MediaSource:
var source = MediaSource.CreateFromUri(mediaUri);
var timedTextSource = TimedTextSource.CreateFromUri(timedTextUri);
source.ExternalTimedTextSources.Add(timedTextSource);
For full working example see this Windows Universal sample on GitHub.

Related

How to find combine file name with file path for WMP url?

I'm making a music player. It has two forms; first one is the typical play/stop/pause interface. second form has a checkedlistbox to choose your songs. Once I click a button in the second form, it fill a text file, with each line listing the name of each mp3 I want played.
All the mp3s are in the same folder.
How does one combine each line separately with the file path?
Eg:
song title: Crazy
file path: C:\Users\Me\Desktop\JAM_MACHINE\JAMS
result: C:\Users\Me\Desktop\JAM_MACHINE\JAMS\Crazy.mp3
This is what I did:
string contents = File.ReadAllText(#"C:\Users\Me\Desktop\JAM_MACHINE\record.txt");
var fullPath = Path.GetFullPath(contents + ".mp3");
wplayer.URL = fullPath;
Nothing happens when I press the button that should start playing the song.
I tried replacing fullPath in the third line with a fixed path, i.e. just
wplayer.URL = #"C:\Users\Me\Desktop\JAM_MACHINE\JAMS\Crazy.mp3");
and pressing the button to play it, and it works. So it's not the coding for the play button that's the problem.
In addition, when I press the button, there is supposed to be a button press sound which works when I try the second code. However there is no sound when I try the first code.
Please help me understand. Here's the full codes for your reference:
`public PLAYER()
{
InitializeComponent();
}
WMPLib.WindowsMediaPlayer wplayer = new WMPLib.WindowsMediaPlayer();
WMPLib.WindowsMediaPlayer playSFX = new WMPLib.WindowsMediaPlayer();
WMPLib.WindowsMediaPlayer pauseSFX = new WMPLib.WindowsMediaPlayer(); //initialise all the sounds
DataRecord songRecord = new DataRecord();
private void PLAYER_Load(object sender, EventArgs e)//the event is actually PLAYER_Activated
{
string contents = File.ReadAllText(#"C:\Users\Me\Desktop\JAM_MACHINE\record.txt");
var fullPath = Path.GetFullPath(contents + ".mp3");
wplayer.URL = fullPath;
playSFX.URL = #"C:\Users\Me\Desktop\JAM_MACHINE\PLAY.wav";
pauseSFX.URL = #"C:\Users\Me\Desktop\JAM_MACHINE\PAUSE.wav";
wplayer.controls.stop();
playSFX.controls.stop();
pauseSFX.controls.stop();
}
private void playbtn_Click(object sender, EventArgs e)
{
playSFX.controls.play();
wplayer.controls.play();
}
private void pausebtn_Click(object sender, EventArgs e)
{
pauseSFX.controls.play();
wplayer.controls.pause();
}
private void stopbtn_Click(object sender, EventArgs e)
{
pauseSFX.controls.play();
wplayer.controls.stop();
this.Visible = false;
JAMS JAMS = new JAMS();
JAMS.ShowDialog();
if(JAMS.Visible == false)//if jams isnt open, open it
{
this.Visible = true;
}
}`
If your records.txt file looks somewhat like this:
myFirstSong
mySecondSong
myThirdSong
Crazy
You can get all the filepaths by doing the following:
private IEnumerable<string> GetFileList(string pathToRecords)
{
var directory = new FileInfo(pathToRecords).DirectoryName;
var songNames = File.ReadAllLines(pathToRecords);
var filePaths = songNames.Select(songName => Path.Combine(directory, songName + ".mp3"));
return filePaths;
}
If pathToRecords is "c:\x\records.txt" that returns you a list that looks like this.
c:\x\myFirstSong.mp3
c:\x\mySecondSong.mp3
c:\x\myThirdSong.mp3
c:\x\Crazy.mp3
Then you can set the player url to one of the paths in the list:
var songPaths = GetFileList(#"c:\x\records.txt").ToList();
var firstSong = songPaths[0];
wplayer.URL = firstSong;
And so on for the remaining songs in the list.

Automatically refresh file list in a UWP app when files moved/added/renamed

I'm working on a simple music player app for UWP and I have a couple of questions.
First of all here's my code
private async void Page_Loaded(object sender, RoutedEventArgs e)
{
StorageFolder folder = KnownFolders.MusicLibrary;
await RetrieveFilesInFolders(folder);
}
private async Task RetrieveFilesInFolders(StorageFolder parent)
{
foreach (var file in await parent.GetFilesAsync())
{
if (file.FileType == ".mp3")
{
var songProperties = await file.Properties.GetMusicPropertiesAsync();
var currentThumb = await file.GetThumbnailAsync(ThumbnailMode.MusicView, 200, ThumbnailOptions.UseCurrentScale);
var albumCover = new BitmapImage();
albumCover.SetSource(currentThumb);
var song = new Song();
song.Title = songProperties.Title;
song.Artist = songProperties.Artist;
song.Album = songProperties.Album;
song.AlbumCover = albumCover;
song.SongFile = file;
song.FileName = file.Name;
SongUC songUc = new SongUC(song);
sp1.Children.Add(songUc);
}
}
foreach (var folder in await parent.GetFoldersAsync())
{
await RetrieveFilesInFolders(folder);
}
}
User control ctor
public SongUC(Song song)
{
this.InitializeComponent();
txtTitle.Text = song.Title;
txtAlbum.Text = song.Album;
txtArtist.Text = song.Artist;
txtName.Text = song.FileName;
imgAlbumArt.Source = song.AlbumCover;
}
How can I refresh the returned files automatically whenever a new song is added or moved or renamed
Is firing the RetrieveFilesInFolders method in the PageLoaded event handler the best way to get all songs. Or will it slow down the app if there's a huge collection of music in the music folder
How can I use animations whenever the files are retrieved to make things look nicer
As stated here, take a look at file queries and their ContentsChanged event.
Use a FileSystemWatcher:
https://msdn.microsoft.com/en-us/library/system.io.filesystemwatcher(v=vs.110).aspx
It allows you to catch events when files are being moved, deleted and renamed.

Turn on torch with MediaCapture on Windows Phone Silverlight 8.1

My problem is quite simple, I cannot turn on the flash light with the MediaCapture API from windows phone 8.1. (I succedded with the 8.0 API)
I built a very simple project with 2 buttons, one to toggle the FlashControl and the other one to toggle TorchControl.
There is no crash, no exception. My phones support FlashControl and TorchControl. I also debug step-by-step and everything looks good, values are changed when buttons are clicked.
Here is my code:
MediaCapture m_captureManager;
public MainPage()
{
InitializeComponent();
}
private static async Task<DeviceInformation> GetCameraID(Windows.Devices.Enumeration.Panel desiredCamera)
{
DeviceInformation deviceID = (await DeviceInformation.FindAllAsync(DeviceClass.VideoCapture))
.FirstOrDefault(x => x.EnclosureLocation != null && x.EnclosureLocation.Panel == desiredCamera);
if (deviceID != null) return deviceID;
else throw new Exception(string.Format("Camera {0} doesn't exist", desiredCamera));
}
protected async override void OnNavigatedTo(NavigationEventArgs e)
{
var cameraID = await GetCameraID(Windows.Devices.Enumeration.Panel.Back);
m_captureManager = new MediaCapture();
await m_captureManager.InitializeAsync(new MediaCaptureInitializationSettings
{
StreamingCaptureMode = StreamingCaptureMode.Video,
PhotoCaptureSource = PhotoCaptureSource.VideoPreview,
AudioDeviceId = string.Empty,
VideoDeviceId = cameraID.Id
});
}
private void button_ClickTorch(object sender, RoutedEventArgs e)
{
var torch = m_captureManager.VideoDeviceController.TorchControl;
if (torch.Supported)
{
if (torch.Enabled)
torch.Enabled = false;
else
torch.Enabled = true;
}
}
private void button_ClickFlash(object sender, RoutedEventArgs e)
{
if (captureManager.VideoDeviceController.FlashControl.Supported)
{
if (captureManager.VideoDeviceController.FlashControl.Enabled)
captureManager.VideoDeviceController.FlashControl.Enabled = false;
else
captureManager.VideoDeviceController.FlashControl.Enabled = true;
}
}
It's a simple piece of code and I cannot make it works... I was quite desperate so I tried to toggle by using an intermediate object and without, as you can see, but it did not change the result (which is in conformity).
I finally found out what was wrong. To be able to use camera services, we have to start a preview.
Since with Silverlight we can’t use a CaptureElement, we have to use a CustomPreviewSink with a VideoBrush
This is how to do it ( from microsoft doc)
private async void StartPreview()
{
previewSink = new Windows.Phone.Media.Capture.MediaCapturePreviewSink();
// List of supported video preview formats to be used by the default preview format selector.
var supportedVideoFormats = new List<string> { "nv12", "rgb32" };
// Find the supported preview format
var availableMediaStreamProperties = mediaCaptureManager.VideoDeviceController.GetAvailableMediaStreamProperties(
Windows.Media.Capture.MediaStreamType.VideoPreview)
.OfType<Windows.Media.MediaProperties.VideoEncodingProperties>()
.Where(p => p != null
&& !String.IsNullOrEmpty(p.Subtype)
&& supportedVideoFormats.Contains(p.Subtype.ToLower()))
.ToList();
var previewFormat = availableMediaStreamProperties.FirstOrDefault();
// Start Preview stream
await mediaCaptureManager.VideoDeviceController.SetMediaStreamPropertiesAsync(
Windows.Media.Capture.MediaStreamType.VideoPreview, previewFormat);
await mediaCaptureManager.StartPreviewToCustomSinkAsync(
new Windows.Media.MediaProperties.MediaEncodingProfile { Video = previewFormat }, previewSink);
// Set the source of the VideoBrush used for your preview
Microsoft.Devices.CameraVideoBrushExtensions.SetSource(viewfinderBrush, previewSink);
}
Add this piece of code to previous code and it will work. The important point is to start the preview before changing any parameters

How to create an audio Playlist in C# , Windows 8 App

private async void Set_Up_Media_Player()
{
var Location = Package.Current.InstalledLocation;
var Folder = await Location.GetFolderAsync("Music");
this._Start = await Folder.GetFileAsync("start.wav");
this._Loop = await Folder.GetFileAsync("loop.wav");
this._Stream = await _Start.OpenAsync(FileAccessMode.Read);
_Music = new MediaElement() { AutoPlay = true, IsLooping = false };
_Music.SetSource(_Stream, _Start.ContentType);
_Music.MediaEnded += Music_MediaEnded;
this._Stream = await _Loop.OpenAsync(FileAccessMode.Read);
}
void Music_MediaEnded(object sender, RoutedEventArgs e)
{
Music.SetSource(this._Stream, this._Loop.ContentType);
Music.IsLooping = true;
}
I'm trying to get the MediaElement _Music To first play the _Start song then transition into the second _Loop song. I have tried adding an event Handler Music_MediaEnded but the _Start song ends and the event is never called.
How do I create a playlist so that the MediaElement will automatically transition to the next song?
As the media ends in the handler set the AutoPlay property of the MediaElement true.
i.e., after setting a new source write
Music.AutoPlay=true;

Loading an image file in WinRT

I have a WinRT project, and am getting an error when trying to preview an image. I have set capabilities to allow access to the Pictures library, and am using the following code:
var file = await Windows.Storage.KnownFolders.PicturesLibrary.GetFileAsync(path);
var fileStream = await file.OpenAsync(Windows.Storage.FileAccessMode.Read);
var img = new BitmapImage();
img.SetSource(fileStream);
This error occurs on the first line:
A first chance exception of type 'System.Runtime.InteropServices.COMException' occurred in mscorlib.dll
Additional information: Error HRESULT E_FAIL has been returned from a call to a COM component.
I've tried other operations, such as folder.GetFilesAsync() with the same error. Is there another or capability that I need to allow this functionality to work correctly?
EDIT:
Based on #L.T.s answer, I tried some other capabilities. The following gives me the same error:
var folder = KnownFolders.PicturesLibrary;
var files = await folder.GetFilesAsync();
However (obviously, providing I provide the Music capability) this does not:
var testfolder = KnownFolders.MusicLibrary;
var files = await testfolder.GetFilesAsync();
I don't doubt that this it something specific to my Pictures library, but I have no idea what that could be.
If your cost is just to preview an image. You can use this
Uri uri = new Uri("ms-appx:///Assets/test.png");
BitmapImage bitmap = new BitmapImage(uri);
Image image = new Image();
image.Source = bitmap;
and add the image to a canvas. But you need to add your test.png to your projects assets first or you can change the Uri to the location of your test image.
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<Image Name="img"></Image>
<Button Name="btn" Click="Btn_OnClick"></Button>
</Grid>
private async void Btn_OnClick(object sender, RoutedEventArgs e)
{
var file = await Windows.Storage.KnownFolders.PicturesLibrary.GetFileAsync("images.jpg");
var fileStream = await file.OpenAsync(Windows.Storage.FileAccessMode.Read);
var image = new BitmapImage();
image.SetSource(fileStream);
img.Source = image;
}
I have windows8.1 and with vs.2013. Don't see any error. Could be something else?
//////to load an image file just do this
//put this in your app.xaml
protected override void OnActivated(IActivatedEventArgs args)
{
var root = Window.Current.Content as Frame;
var mainPage = root.Content as MainPage;
if (mainPage != null && args is FileOpenPickerContinuationEventArgs)
{
mainPage.ContinueFileOpenPicker(args as FileOpenPickerContinuationEventArgs);
}
}
//and this in your btn event
private void Button_Click(object sender, RoutedEventArgs e)
{
var openPicker = new FileOpenPicker
{
ViewMode = PickerViewMode.Thumbnail,
SuggestedStartLocation = PickerLocationId.PicturesLibrary
};
openPicker.FileTypeFilter.Add(".jpg");
openPicker.PickSingleFileAndContinue();
}
//and this in you page
public void ContinueFileOpenPicker(FileOpenPickerContinuationEventArgs fileOpenPickerContinuationEventArgs)
{
if (fileOpenPickerContinuationEventArgs.Files != null)
{
// Do something with selected file/s
}
}

Categories

Resources