Skip to content

Commit d7ab26b

Browse files
No artwork shown in-app on MediaElement using meta data #2659 (#2666)
* No artwork shown in-app on MediaElement using meta data #2659 * Remove duplicate code * Refactor `GetBytesFromMetadataArtworkUrl`, Update Sample App * `dotnet format` * Remove Unused Code --------- Co-authored-by: Brandon Minnick <[email protected]>
1 parent 514b577 commit d7ab26b

File tree

3 files changed

+121
-18
lines changed

3 files changed

+121
-18
lines changed

samples/CommunityToolkit.Maui.Sample/Pages/Views/MediaElement/MediaElementPage.xaml.cs

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,6 @@ namespace CommunityToolkit.Maui.Sample.Pages.Views;
1010

1111
public partial class MediaElementPage : BasePage<MediaElementViewModel>
1212
{
13-
readonly ILogger logger;
14-
1513
const string loadOnlineMp4 = "Load Online MP4";
1614
const string loadHls = "Load HTTP Live Stream (HLS)";
1715
const string loadLocalResource = "Load Local Resource";
@@ -22,11 +20,18 @@ public partial class MediaElementPage : BasePage<MediaElementViewModel>
2220
const string hlsStreamTestUrl = "https://mtoczko.github.io/hls-test-streams/test-gap/playlist.m3u8";
2321
const string hal9000AudioUrl = "https://github.com/prof3ssorSt3v3/media-sample-files/raw/master/hal-9000.mp3";
2422

25-
public MediaElementPage(MediaElementViewModel viewModel, ILogger<MediaElementPage> logger) : base(viewModel)
23+
24+
readonly ILogger logger;
25+
readonly IDeviceInfo deviceInfo;
26+
readonly IFileSystem fileSystem;
27+
28+
public MediaElementPage(MediaElementViewModel viewModel, IFileSystem fileSystem, IDeviceInfo deviceInfo, ILogger<MediaElementPage> logger) : base(viewModel)
2629
{
2730
InitializeComponent();
2831

2932
this.logger = logger;
33+
this.deviceInfo = deviceInfo;
34+
this.fileSystem = fileSystem;
3035
MediaElement.PropertyChanged += MediaElement_PropertyChanged;
3136
}
3237

@@ -163,6 +168,9 @@ async void ChangeSourceClicked(Object sender, EventArgs e)
163168
var result = await DisplayActionSheet("Choose a source", "Cancel", null,
164169
loadOnlineMp4, loadHls, loadLocalResource, resetSource, loadMusic);
165170

171+
MediaElement.Stop();
172+
MediaElement.Source = null;
173+
166174
switch (result)
167175
{
168176
case loadOnlineMp4:
@@ -246,15 +254,36 @@ async void ChangeAspectClicked(object? sender, EventArgs e)
246254
async void DisplayPopup(object sender, EventArgs e)
247255
{
248256
MediaElement.Pause();
257+
258+
MediaSource source;
259+
260+
if (deviceInfo.Platform == DevicePlatform.Android)
261+
{
262+
source = MediaSource.FromResource("AndroidVideo.mp4");
263+
}
264+
else if (deviceInfo.Platform == DevicePlatform.MacCatalyst
265+
|| deviceInfo.Platform == DevicePlatform.iOS
266+
|| deviceInfo.Platform == DevicePlatform.macOS)
267+
{
268+
source = MediaSource.FromResource("AppleVideo.mp4");
269+
}
270+
else
271+
{
272+
source = MediaSource.FromResource("WindowsVideo.mp4");
273+
}
274+
249275
var popupMediaElement = new MediaElement
250276
{
251277
AndroidViewType = AndroidViewType.SurfaceView,
252-
Source = MediaSource.FromResource("AppleVideo.mp4"),
278+
Source = source,
279+
MetadataArtworkUrl = "dotnet_bot.png",
253280
ShouldAutoPlay = true,
254281
ShouldShowPlaybackControls = true,
255282
};
256283

257284
await this.ShowPopupAsync(popupMediaElement);
285+
258286
popupMediaElement.Stop();
287+
popupMediaElement.Source = null;
259288
}
260289
}
58.4 KB
Loading

src/CommunityToolkit.Maui.MediaElement/Views/MediaManager.android.cs

Lines changed: 88 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using System.Diagnostics.CodeAnalysis;
1+
using System.Diagnostics;
2+
using System.Diagnostics.CodeAnalysis;
23
using Android.Content;
34
using Android.Views;
45
using Android.Widget;
@@ -536,27 +537,97 @@ protected override void Dispose(bool disposing)
536537
}
537538
}
538539

539-
static async Task<byte[]> GetBytesFromMetadataArtworkUrl(string? url, CancellationToken cancellationToken = default)
540+
static async Task<byte[]> GetBytesFromMetadataArtworkUrl(string url, CancellationToken cancellationToken = default)
540541
{
541-
byte[] artworkData = [];
542+
if (string.IsNullOrWhiteSpace(url))
543+
{
544+
return [];
545+
}
546+
547+
Stream? stream = null;
548+
Uri.TryCreate(url, UriKind.Absolute, out var uri);
549+
542550
try
543551
{
544-
var response = await client.GetAsync(url, cancellationToken).ConfigureAwait(false);
545-
var stream = response.IsSuccessStatusCode ? await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false) : null;
552+
byte[] artworkData = [];
553+
long? contentLength = null;
554+
555+
// HTTP or HTTPS URL
556+
if (uri is not null &&
557+
(uri.Scheme == Uri.UriSchemeHttp || uri.Scheme == Uri.UriSchemeHttps))
558+
{
559+
var request = new HttpRequestMessage(HttpMethod.Head, url);
560+
var contentLengthResponse = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false);
561+
contentLength = contentLengthResponse.Content.Headers.ContentLength ?? 0;
546562

547-
if (stream is null)
563+
var response = await client.GetAsync(url, HttpCompletionOption.ResponseContentRead, cancellationToken).ConfigureAwait(false);
564+
stream = response.IsSuccessStatusCode ? await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false) : null;
565+
}
566+
// Absolute File Path
567+
else if (uri is not null && uri.Scheme == Uri.UriSchemeFile)
548568
{
549-
return artworkData;
569+
var normalizedFilePath = NormalizeFilePath(url);
570+
571+
stream = File.Open(normalizedFilePath, FileMode.Create);
572+
contentLength = await GetByteCountFromStream(stream, cancellationToken);
550573
}
574+
// Relative File Path
575+
else if (Uri.TryCreate(url, UriKind.Relative, out _))
576+
{
577+
var normalizedFilePath = NormalizeFilePath(url);
551578

552-
using var memoryStream = new MemoryStream();
553-
await stream.CopyToAsync(memoryStream, cancellationToken).ConfigureAwait(false);
554-
var bytes = memoryStream.ToArray();
555-
return bytes;
579+
stream = Platform.AppContext.Assets?.Open(normalizedFilePath) ?? throw new InvalidOperationException("Assets cannot be null");
580+
contentLength = await GetByteCountFromStream(stream, cancellationToken);
581+
}
582+
583+
if (stream is not null)
584+
{
585+
if (!contentLength.HasValue)
586+
{
587+
throw new InvalidOperationException($"{nameof(contentLength)} must be set when {nameof(stream)} is not null");
588+
}
589+
590+
artworkData = new byte[contentLength.Value];
591+
using var memoryStream = new MemoryStream(artworkData);
592+
await stream.CopyToAsync(memoryStream, cancellationToken).ConfigureAwait(false);
593+
}
594+
595+
return artworkData;
556596
}
557-
catch
597+
catch (Exception e)
558598
{
559-
return artworkData;
599+
Trace.WriteLine($"Unable to retrieve {nameof(MediaElement.MetadataArtworkUrl)} for {url}.{e}\n");
600+
return [];
601+
}
602+
finally
603+
{
604+
if (stream is not null)
605+
{
606+
stream.Close();
607+
await stream.DisposeAsync();
608+
}
609+
}
610+
611+
static string NormalizeFilePath(string filePath) => filePath.Replace('\\', Path.DirectorySeparatorChar).Replace('/', Path.DirectorySeparatorChar);
612+
613+
static async ValueTask<long> GetByteCountFromStream(Stream stream, CancellationToken token)
614+
{
615+
if (stream.CanSeek)
616+
{
617+
return stream.Length;
618+
}
619+
620+
long countedStreamBytes = 0;
621+
622+
var buffer = new byte[8192];
623+
int bytesRead;
624+
625+
while ((bytesRead = await stream.ReadAsync(buffer, token)) > 0)
626+
{
627+
countedStreamBytes += bytesRead;
628+
}
629+
630+
return countedStreamBytes;
560631
}
561632
}
562633

@@ -636,7 +707,10 @@ void StopService(in BoundServiceConnection boundServiceConnection)
636707
mediaMetaData.SetArtist(MediaElement.MetadataArtist);
637708
mediaMetaData.SetTitle(MediaElement.MetadataTitle);
638709
var data = await GetBytesFromMetadataArtworkUrl(MediaElement.MetadataArtworkUrl, cancellationToken).ConfigureAwait(true);
639-
mediaMetaData.SetArtworkData(data, (Java.Lang.Integer)MediaMetadata.PictureTypeFrontCover);
710+
if (data is not null && data.Length > 0)
711+
{
712+
mediaMetaData.SetArtworkData(data, (Java.Lang.Integer)MediaMetadata.PictureTypeFrontCover);
713+
}
640714

641715
mediaItem = new MediaItem.Builder();
642716
mediaItem.SetUri(url);

0 commit comments

Comments
 (0)