From c34f5cd519fef533b273d5ec6bed598e26ac6c8c Mon Sep 17 00:00:00 2001 From: James Crutchley Date: Wed, 13 Aug 2025 21:57:22 -0700 Subject: [PATCH 1/4] [add] Enhance DashMediaSource and TrackGroupArray with fluent API and usability improvements --- .../Additions/DashMediaSource.Factory.cs | 19 +++++++++ .../DashMediaSourceFactoryExtensions.cs | 39 +++++++++++++++++++ .../PublicAPI/PublicAPI.Unshipped.txt | 3 ++ .../Transforms/Metadata.xml | 4 +- .../Additions/TrackGroupArray.cs | 14 +++++++ .../TrackSelectionArray.additions.cs | 35 +++++++++++++++++ .../PublicAPI/PublicAPI.Unshipped.txt | 5 +++ 7 files changed, 117 insertions(+), 2 deletions(-) create mode 100644 source/androidx.media3/media3-exoplayer-dash/Additions/DashMediaSource.Factory.cs create mode 100644 source/androidx.media3/media3-exoplayer-dash/Additions/DashMediaSourceFactoryExtensions.cs create mode 100644 source/androidx.media3/media3-exoplayer/Additions/TrackGroupArray.cs create mode 100644 source/androidx.media3/media3-exoplayer/Additions/TrackSelectionArray.additions.cs diff --git a/source/androidx.media3/media3-exoplayer-dash/Additions/DashMediaSource.Factory.cs b/source/androidx.media3/media3-exoplayer-dash/Additions/DashMediaSource.Factory.cs new file mode 100644 index 000000000..b6d2f6b03 --- /dev/null +++ b/source/androidx.media3/media3-exoplayer-dash/Additions/DashMediaSource.Factory.cs @@ -0,0 +1,19 @@ +// Additions to adjust fluent interface return types to satisfy IMediaSourceMediaSourceIFactory +using AndroidX.Media3.ExoPlayer.Drm; +using AndroidX.Media3.ExoPlayer.Upstream; +using AndroidX.Media3.ExoPlayer.Source; + +namespace AndroidX.Media3.ExoPlayer.Dash; + +public sealed partial class DashMediaSource +{ + public sealed partial class Factory : IMediaSourceMediaSourceIFactory + { + // Explicit interface implementations returning the interface type for fluent chaining + IMediaSourceMediaSourceIFactory? IMediaSourceMediaSourceIFactory.SetDrmSessionManagerProvider(IDrmSessionManagerProvider? drmSessionManagerProvider) + => SetDrmSessionManagerProvider(drmSessionManagerProvider); + + IMediaSourceMediaSourceIFactory? IMediaSourceMediaSourceIFactory.SetLoadErrorHandlingPolicy(ILoadErrorHandlingPolicy? loadErrorHandlingPolicy) + => SetLoadErrorHandlingPolicy(loadErrorHandlingPolicy); + } +} diff --git a/source/androidx.media3/media3-exoplayer-dash/Additions/DashMediaSourceFactoryExtensions.cs b/source/androidx.media3/media3-exoplayer-dash/Additions/DashMediaSourceFactoryExtensions.cs new file mode 100644 index 000000000..2ca5d3642 --- /dev/null +++ b/source/androidx.media3/media3-exoplayer-dash/Additions/DashMediaSourceFactoryExtensions.cs @@ -0,0 +1,39 @@ +// Extensions to improve usability of DashMediaSource.Factory in .NET bindings. +// Provides fluent-style chaining methods that return the concrete Factory type instead of the +// generic IMediaSourceMediaSourceIFactory interface, which is the Java-exposed return type. +// This lets callers write: new DashMediaSource.Factory(ds).SetDrmSessionManagerProviderChained(...).SetLoadErrorHandlingPolicyChained(...); + +using AndroidX.Media3.ExoPlayer.Dash; +using AndroidX.Media3.ExoPlayer.Drm; +using AndroidX.Media3.ExoPlayer.Upstream; + +namespace AndroidX.Media3.ExoPlayer.Dash +{ + /// + /// Extension helpers for to enable fluent chaining + /// with strongly-typed return values. The original Java API returns a MediaSource.Factory + /// interface causing the bound method to return IMediaSourceMediaSourceIFactory. + /// These helpers wrap those calls and return the concrete . + /// + public static class DashMediaSourceFactoryExtensions + { + /// + /// Sets the DRM session manager provider and returns the same instance for chaining. + /// + public static DashMediaSource.Factory SetDrmSessionManagerProviderChained(this DashMediaSource.Factory factory, IDrmSessionManagerProvider? provider) + { + // Call the original bound method (returns the interface) and ignore its return to preserve fluent API. + factory.SetDrmSessionManagerProvider(provider); + return factory; + } + + /// + /// Sets the load error handling policy and returns the same instance for chaining. + /// + public static DashMediaSource.Factory SetLoadErrorHandlingPolicyChained(this DashMediaSource.Factory factory, ILoadErrorHandlingPolicy? policy) + { + factory.SetLoadErrorHandlingPolicy(policy); + return factory; + } + } +} diff --git a/source/androidx.media3/media3-exoplayer-dash/PublicAPI/PublicAPI.Unshipped.txt b/source/androidx.media3/media3-exoplayer-dash/PublicAPI/PublicAPI.Unshipped.txt index 79ee261b4..a30e3813d 100644 --- a/source/androidx.media3/media3-exoplayer-dash/PublicAPI/PublicAPI.Unshipped.txt +++ b/source/androidx.media3/media3-exoplayer-dash/PublicAPI/PublicAPI.Unshipped.txt @@ -27,6 +27,7 @@ AndroidX.Media3.ExoPlayer.Dash.DashMediaSource.Factory.SetMinLiveStartPositionUs AndroidX.Media3.ExoPlayer.Dash.DashMediaSource.Factory.SetSubtitleParserFactory(AndroidX.Media3.Extractor.Text.ISubtitleParserFactory? subtitleParserFactory) -> AndroidX.Media3.ExoPlayer.Dash.DashMediaSource.Factory? AndroidX.Media3.ExoPlayer.Dash.DashMediaSource.ReplaceManifestUri(Android.Net.Uri? manifestUri) -> void AndroidX.Media3.ExoPlayer.Dash.DashMediaSource.UpdateMediaItem(AndroidX.Media3.Common.MediaItem? mediaItem) -> void +AndroidX.Media3.ExoPlayer.Dash.DashMediaSourceFactoryExtensions AndroidX.Media3.ExoPlayer.Dash.DashUtil AndroidX.Media3.ExoPlayer.Dash.DashWrappingSegmentIndex AndroidX.Media3.ExoPlayer.Dash.DashWrappingSegmentIndex.DashWrappingSegmentIndex(AndroidX.Media3.Extractor.ChunkIndex? chunkIndex, long timeOffsetUs) -> void @@ -395,6 +396,8 @@ override AndroidX.Media3.ExoPlayer.Dash.Offline.DashDownloader.JniPeerMembers.ge override AndroidX.Media3.ExoPlayer.Dash.PlayerEmsgHandler.JniPeerMembers.get -> Java.Interop.JniPeerMembers! override AndroidX.Media3.ExoPlayer.Dash.PlayerEmsgHandler.PlayerTrackEmsgHandler.JniPeerMembers.get -> Java.Interop.JniPeerMembers! static AndroidX.Media3.ExoPlayer.Dash.BaseUrlExclusionList.GetPriorityCount(System.Collections.Generic.IList? baseUrls) -> int +static AndroidX.Media3.ExoPlayer.Dash.DashMediaSourceFactoryExtensions.SetDrmSessionManagerProviderChained(this AndroidX.Media3.ExoPlayer.Dash.DashMediaSource.Factory! factory, AndroidX.Media3.ExoPlayer.Drm.IDrmSessionManagerProvider? provider) -> AndroidX.Media3.ExoPlayer.Dash.DashMediaSource.Factory! +static AndroidX.Media3.ExoPlayer.Dash.DashMediaSourceFactoryExtensions.SetLoadErrorHandlingPolicyChained(this AndroidX.Media3.ExoPlayer.Dash.DashMediaSource.Factory! factory, AndroidX.Media3.ExoPlayer.Upstream.ILoadErrorHandlingPolicy? policy) -> AndroidX.Media3.ExoPlayer.Dash.DashMediaSource.Factory! static AndroidX.Media3.ExoPlayer.Dash.DashUtil.BuildDataSpec(AndroidX.Media3.ExoPlayer.Dash.Manifest.Representation? representation, AndroidX.Media3.ExoPlayer.Dash.Manifest.RangedUri? requestUri, int flags) -> AndroidX.Media3.DataSource.DataSpec? static AndroidX.Media3.ExoPlayer.Dash.DashUtil.BuildDataSpec(AndroidX.Media3.ExoPlayer.Dash.Manifest.Representation? representation, string? baseUrl, AndroidX.Media3.ExoPlayer.Dash.Manifest.RangedUri? requestUri, int flags) -> AndroidX.Media3.DataSource.DataSpec? static AndroidX.Media3.ExoPlayer.Dash.DashUtil.BuildDataSpec(AndroidX.Media3.ExoPlayer.Dash.Manifest.Representation? representation, string? baseUrl, AndroidX.Media3.ExoPlayer.Dash.Manifest.RangedUri? requestUri, int flags, System.Collections.Generic.IDictionary? httpRequestHeaders) -> AndroidX.Media3.DataSource.DataSpec? diff --git a/source/androidx.media3/media3-exoplayer-dash/Transforms/Metadata.xml b/source/androidx.media3/media3-exoplayer-dash/Transforms/Metadata.xml index 00011b88c..dbf52818f 100644 --- a/source/androidx.media3/media3-exoplayer-dash/Transforms/Metadata.xml +++ b/source/androidx.media3/media3-exoplayer-dash/Transforms/Metadata.xml @@ -52,13 +52,13 @@ path="/api/package[@name='androidx.media3.exoplayer.dash']/class[@name='DashMediaSource.Factory']/method[@name='setDrmSessionManagerProvider' and count(parameter)=1 and parameter[1][@type='androidx.media3.exoplayer.drm.DrmSessionManagerProvider']]" name="managedReturn" > - AndroidX.Media3.ExoPlayer.Source.IMediaSourceMediaSourceIFactory + AndroidX.Media3.ExoPlayer.Source.IMediaSourceMediaSourceIFactory - AndroidX.Media3.ExoPlayer.Source.IMediaSourceMediaSourceIFactory + AndroidX.Media3.ExoPlayer.Source.IMediaSourceMediaSourceIFactory diff --git a/source/androidx.media3/media3-exoplayer/Additions/TrackGroupArray.cs b/source/androidx.media3/media3-exoplayer/Additions/TrackGroupArray.cs new file mode 100644 index 000000000..0e5f44c68 --- /dev/null +++ b/source/androidx.media3/media3-exoplayer/Additions/TrackGroupArray.cs @@ -0,0 +1,14 @@ +using AndroidX.Media3.Common; + +namespace AndroidX.Media3.ExoPlayer.Source; + +// Convenience additions to match common ExoPlayer Java samples (trackGroups.get(i)) +// and the intuitive GetGroup(...) naming some developers attempt to use. +public partial class TrackGroupArray +{ + // Alias matching the user's attempted call tracks.GetGroup(i) + public TrackGroup? GetGroup(int index) => Get(index); + + // Indexer for more idiomatic C# access: trackGroups[i] + public TrackGroup? this[int index] => Get(index); +} diff --git a/source/androidx.media3/media3-exoplayer/Additions/TrackSelectionArray.additions.cs b/source/androidx.media3/media3-exoplayer/Additions/TrackSelectionArray.additions.cs new file mode 100644 index 000000000..cd04f1ca8 --- /dev/null +++ b/source/androidx.media3/media3-exoplayer/Additions/TrackSelectionArray.additions.cs @@ -0,0 +1,35 @@ +// Additions to improve .NET ergonomics for TrackSelectionArray +// Provides indexer and IEnumerable implementation for iterating track selections. + +using System.Collections; +using System.Collections.Generic; + +namespace AndroidX.Media3.ExoPlayer.TrackSelection +{ + public partial class TrackSelectionArray : IEnumerable + { + public ITrackSelection? this[int index] => Get(index); + + public IEnumerator GetEnumerator() + { + for (int i = 0; i < Length; i++) + { + yield return Get(i); + } + } + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + /// + /// Returns all non-null track selections of type . + /// + public IEnumerable OfType() where T : class, ITrackSelection + { + for (int i = 0; i < Length; i++) + { + if (Get(i) is T t) + yield return t; + } + } + } +} diff --git a/source/androidx.media3/media3-exoplayer/PublicAPI/PublicAPI.Unshipped.txt b/source/androidx.media3/media3-exoplayer/PublicAPI/PublicAPI.Unshipped.txt index f8251de38..fc041608f 100644 --- a/source/androidx.media3/media3-exoplayer/PublicAPI/PublicAPI.Unshipped.txt +++ b/source/androidx.media3/media3-exoplayer/PublicAPI/PublicAPI.Unshipped.txt @@ -3446,12 +3446,14 @@ AndroidX.Media3.ExoPlayer.Source.TimelineWithUpdatedMediaItem AndroidX.Media3.ExoPlayer.Source.TimelineWithUpdatedMediaItem.TimelineWithUpdatedMediaItem(AndroidX.Media3.Common.Timeline? timeline, AndroidX.Media3.Common.MediaItem? mediaItem) -> void AndroidX.Media3.ExoPlayer.Source.TrackGroupArray AndroidX.Media3.ExoPlayer.Source.TrackGroupArray.Get(int index) -> AndroidX.Media3.Common.TrackGroup? +AndroidX.Media3.ExoPlayer.Source.TrackGroupArray.GetGroup(int index) -> AndroidX.Media3.Common.TrackGroup? AndroidX.Media3.ExoPlayer.Source.TrackGroupArray.IndexOf(AndroidX.Media3.Common.TrackGroup? group) -> int AndroidX.Media3.ExoPlayer.Source.TrackGroupArray.IsEmpty.get -> bool AndroidX.Media3.ExoPlayer.Source.TrackGroupArray.Length.get -> int AndroidX.Media3.ExoPlayer.Source.TrackGroupArray.Length.set -> void AndroidX.Media3.ExoPlayer.Source.TrackGroupArray.ToBundle() -> Android.OS.Bundle? AndroidX.Media3.ExoPlayer.Source.TrackGroupArray.TrackGroupArray(params AndroidX.Media3.Common.TrackGroup![]? trackGroups) -> void +AndroidX.Media3.ExoPlayer.Source.TrackGroupArray.this[int index].get -> AndroidX.Media3.Common.TrackGroup? AndroidX.Media3.ExoPlayer.Source.UnrecognizedInputFormatException AndroidX.Media3.ExoPlayer.Source.UnrecognizedInputFormatException.UnrecognizedInputFormatException(nint javaReference, Android.Runtime.JniHandleOwnership transfer) -> void AndroidX.Media3.ExoPlayer.Source.UnrecognizedInputFormatException.UnrecognizedInputFormatException(string? message, Android.Net.Uri? uri) -> void @@ -3705,9 +3707,12 @@ AndroidX.Media3.ExoPlayer.TrackSelection.RandomTrackSelection.RandomTrackSelecti AndroidX.Media3.ExoPlayer.TrackSelection.TrackSelectionArray AndroidX.Media3.ExoPlayer.TrackSelection.TrackSelectionArray.Get(int index) -> AndroidX.Media3.ExoPlayer.TrackSelection.ITrackSelection? AndroidX.Media3.ExoPlayer.TrackSelection.TrackSelectionArray.GetAll() -> AndroidX.Media3.ExoPlayer.TrackSelection.ITrackSelection![]? +AndroidX.Media3.ExoPlayer.TrackSelection.TrackSelectionArray.GetEnumerator() -> System.Collections.Generic.IEnumerator! AndroidX.Media3.ExoPlayer.TrackSelection.TrackSelectionArray.Length.get -> int AndroidX.Media3.ExoPlayer.TrackSelection.TrackSelectionArray.Length.set -> void +AndroidX.Media3.ExoPlayer.TrackSelection.TrackSelectionArray.OfType() -> System.Collections.Generic.IEnumerable! AndroidX.Media3.ExoPlayer.TrackSelection.TrackSelectionArray.TrackSelectionArray(params AndroidX.Media3.ExoPlayer.TrackSelection.ITrackSelection![]? trackSelections) -> void +AndroidX.Media3.ExoPlayer.TrackSelection.TrackSelectionArray.this[int index].get -> AndroidX.Media3.ExoPlayer.TrackSelection.ITrackSelection? AndroidX.Media3.ExoPlayer.TrackSelection.TrackSelectionUtil AndroidX.Media3.ExoPlayer.TrackSelection.TrackSelectionUtil.IAdaptiveTrackSelectionFactory AndroidX.Media3.ExoPlayer.TrackSelection.TrackSelectionUtil.IAdaptiveTrackSelectionFactory.CreateAdaptiveTrackSelection(AndroidX.Media3.ExoPlayer.TrackSelection.ExoTrackSelectionDefinition? p0) -> AndroidX.Media3.ExoPlayer.TrackSelection.IExoTrackSelection? From 64d9d265ea739815b2e43702f1e16b77bbd50229 Mon Sep 17 00:00:00 2001 From: James Crutchley Date: Thu, 14 Aug 2025 08:28:14 -0700 Subject: [PATCH 2/4] [add] Implement Tracks class with methods for accessing track groups and improved enumeration --- .../media3-common/Additions/Tracks.cs | 91 +++++++++++++++++++ .../PublicAPI/PublicAPI.Unshipped.txt | 5 + .../TrackSelectionArray.additions.cs | 12 --- .../PublicAPI/PublicAPI.Unshipped.txt | 1 - 4 files changed, 96 insertions(+), 13 deletions(-) create mode 100644 source/androidx.media3/media3-common/Additions/Tracks.cs diff --git a/source/androidx.media3/media3-common/Additions/Tracks.cs b/source/androidx.media3/media3-common/Additions/Tracks.cs new file mode 100644 index 000000000..7bcb146ae --- /dev/null +++ b/source/androidx.media3/media3-common/Additions/Tracks.cs @@ -0,0 +1,91 @@ +using System.Collections.Generic; +using System.Linq; +using Java.Interop; +using Android.Runtime; + +namespace AndroidX.Media3.Common; + +// Convenience additions to make the modern Media3 Tracks API usable following API guidelines. +// Enables: player.getCurrentTracks().getGroups() and iteration over track groups. +public partial class Tracks : System.Collections.Generic.IEnumerable +{ + /// + /// Gets an immutable list of track groups. + /// This implements the missing getGroups() method from the Java API. + /// + /// An immutable list of track groups. + /// + /// This method provides access to the track groups, where each group contains tracks that + /// present the same content but in different formats (different qualities, codecs, languages, etc.). + /// + /// This matches the Java API: public ImmutableList<Tracks.Group> getGroups() + /// + public unsafe IList? GetGroups() + { + // Call the native Java getGroups() method + // Based on the AndroidX Media3 documentation, this should return ImmutableList + const string __id = "getGroups.()Lcom/google/common/collect/ImmutableList;"; + try + { + var __rm = _members.InstanceMethods.InvokeNonvirtualObjectMethod(__id, this, null); + return global::Android.Runtime.JavaList.FromJniHandle(__rm.Handle, JniHandleOwnership.TransferLocalRef); + } + finally + { + global::System.GC.KeepAlive(this); + } + } + + /// + /// Gets the track group at the specified index. + /// This method provides access to individual track groups within the Tracks collection. + /// + /// The index of the group to retrieve. + /// The track group at the specified index. + /// + /// Each group contains tracks that present the same content but in different formats + /// (different qualities, codecs, languages, etc.). + /// + public Tracks.Group? GetGroup(int index) + { + var groups = GetGroups(); + if (groups == null || index < 0 || index >= groups.Count) + return null; + + return groups[index]; + } + + /// + /// Gets the track group at the specified index using indexer syntax. + /// + /// The index of the group to retrieve. + /// The track group at the specified index. + public Tracks.Group? this[int index] => GetGroup(index); + + /// + /// Gets the number of track groups. + /// + public int Count + { + get + { + var groups = GetGroups(); + return groups?.Count ?? 0; + } + } + + /// + /// Returns an enumerator that iterates through the track groups. + /// + /// An enumerator for the track groups. + public IEnumerator GetEnumerator() + { + var groups = GetGroups(); + if (groups == null) + return Enumerable.Empty().GetEnumerator(); + + return groups.GetEnumerator(); + } + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => GetEnumerator(); +} diff --git a/source/androidx.media3/media3-common/PublicAPI/PublicAPI.Unshipped.txt b/source/androidx.media3/media3-common/PublicAPI/PublicAPI.Unshipped.txt index 2633e6e03..1aa4ab231 100644 --- a/source/androidx.media3/media3-common/PublicAPI/PublicAPI.Unshipped.txt +++ b/source/androidx.media3/media3-common/PublicAPI/PublicAPI.Unshipped.txt @@ -1965,6 +1965,10 @@ AndroidX.Media3.Common.TrackSelectionParametersChangedEventArgs.Parameters.get - AndroidX.Media3.Common.TrackSelectionParametersChangedEventArgs.TrackSelectionParametersChangedEventArgs(AndroidX.Media3.Common.TrackSelectionParameters? parameters) -> void AndroidX.Media3.Common.Tracks AndroidX.Media3.Common.Tracks.ContainsType(int trackType) -> bool +AndroidX.Media3.Common.Tracks.Count.get -> int +AndroidX.Media3.Common.Tracks.GetEnumerator() -> System.Collections.Generic.IEnumerator! +AndroidX.Media3.Common.Tracks.GetGroup(int index) -> AndroidX.Media3.Common.Tracks.Group? +AndroidX.Media3.Common.Tracks.GetGroups() -> System.Collections.Generic.IList? AndroidX.Media3.Common.Tracks.Group AndroidX.Media3.Common.Tracks.Group.CopyWithId(string? groupId) -> AndroidX.Media3.Common.Tracks.Group? AndroidX.Media3.Common.Tracks.Group.GetTrackFormat(int trackIndex) -> AndroidX.Media3.Common.Format? @@ -1990,6 +1994,7 @@ AndroidX.Media3.Common.Tracks.IsTypeSupportedOrEmpty(int trackType) -> bool AndroidX.Media3.Common.Tracks.IsTypeSupportedOrEmpty(int trackType, bool allowExceedsCapabilities) -> bool AndroidX.Media3.Common.Tracks.ToBundle() -> Android.OS.Bundle? AndroidX.Media3.Common.Tracks.Tracks(System.Collections.Generic.IList? groups) -> void +AndroidX.Media3.Common.Tracks.this[int index].get -> AndroidX.Media3.Common.Tracks.Group? AndroidX.Media3.Common.TracksChangedEventArgs AndroidX.Media3.Common.TracksChangedEventArgs.Tracks.get -> AndroidX.Media3.Common.Tracks? AndroidX.Media3.Common.TracksChangedEventArgs.TracksChangedEventArgs(AndroidX.Media3.Common.Tracks? tracks) -> void diff --git a/source/androidx.media3/media3-exoplayer/Additions/TrackSelectionArray.additions.cs b/source/androidx.media3/media3-exoplayer/Additions/TrackSelectionArray.additions.cs index cd04f1ca8..bdbdf756b 100644 --- a/source/androidx.media3/media3-exoplayer/Additions/TrackSelectionArray.additions.cs +++ b/source/androidx.media3/media3-exoplayer/Additions/TrackSelectionArray.additions.cs @@ -19,17 +19,5 @@ public partial class TrackSelectionArray : IEnumerable } IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - - /// - /// Returns all non-null track selections of type . - /// - public IEnumerable OfType() where T : class, ITrackSelection - { - for (int i = 0; i < Length; i++) - { - if (Get(i) is T t) - yield return t; - } - } } } diff --git a/source/androidx.media3/media3-exoplayer/PublicAPI/PublicAPI.Unshipped.txt b/source/androidx.media3/media3-exoplayer/PublicAPI/PublicAPI.Unshipped.txt index fc041608f..b911b776a 100644 --- a/source/androidx.media3/media3-exoplayer/PublicAPI/PublicAPI.Unshipped.txt +++ b/source/androidx.media3/media3-exoplayer/PublicAPI/PublicAPI.Unshipped.txt @@ -3710,7 +3710,6 @@ AndroidX.Media3.ExoPlayer.TrackSelection.TrackSelectionArray.GetAll() -> Android AndroidX.Media3.ExoPlayer.TrackSelection.TrackSelectionArray.GetEnumerator() -> System.Collections.Generic.IEnumerator! AndroidX.Media3.ExoPlayer.TrackSelection.TrackSelectionArray.Length.get -> int AndroidX.Media3.ExoPlayer.TrackSelection.TrackSelectionArray.Length.set -> void -AndroidX.Media3.ExoPlayer.TrackSelection.TrackSelectionArray.OfType() -> System.Collections.Generic.IEnumerable! AndroidX.Media3.ExoPlayer.TrackSelection.TrackSelectionArray.TrackSelectionArray(params AndroidX.Media3.ExoPlayer.TrackSelection.ITrackSelection![]? trackSelections) -> void AndroidX.Media3.ExoPlayer.TrackSelection.TrackSelectionArray.this[int index].get -> AndroidX.Media3.ExoPlayer.TrackSelection.ITrackSelection? AndroidX.Media3.ExoPlayer.TrackSelection.TrackSelectionUtil From 97f27448607c9280b17a05f23790fa4350b92d2e Mon Sep 17 00:00:00 2001 From: James Crutchley Date: Thu, 14 Aug 2025 10:47:55 -0700 Subject: [PATCH 3/4] Fixes --- .../Additions/DashMediaSource.Factory.cs | 49 ++++++++++++++----- .../DashMediaSourceFactoryExtensions.cs | 39 --------------- .../PublicAPI/PublicAPI.Unshipped.txt | 2 + .../Transforms/Metadata.xml | 24 ++++----- 4 files changed, 52 insertions(+), 62 deletions(-) delete mode 100644 source/androidx.media3/media3-exoplayer-dash/Additions/DashMediaSourceFactoryExtensions.cs diff --git a/source/androidx.media3/media3-exoplayer-dash/Additions/DashMediaSource.Factory.cs b/source/androidx.media3/media3-exoplayer-dash/Additions/DashMediaSource.Factory.cs index b6d2f6b03..30bf89d32 100644 --- a/source/androidx.media3/media3-exoplayer-dash/Additions/DashMediaSource.Factory.cs +++ b/source/androidx.media3/media3-exoplayer-dash/Additions/DashMediaSource.Factory.cs @@ -1,19 +1,46 @@ -// Additions to adjust fluent interface return types to satisfy IMediaSourceMediaSourceIFactory using AndroidX.Media3.ExoPlayer.Drm; -using AndroidX.Media3.ExoPlayer.Upstream; using AndroidX.Media3.ExoPlayer.Source; +using AndroidX.Media3.ExoPlayer.Upstream; -namespace AndroidX.Media3.ExoPlayer.Dash; - -public sealed partial class DashMediaSource +namespace AndroidX.Media3.ExoPlayer.Dash { - public sealed partial class Factory : IMediaSourceMediaSourceIFactory + public sealed partial class DashMediaSource + { + public sealed partial class Factory + { + // Extension methods for fluent API chaining with concrete return type + public Factory SetDrmSessionManagerProviderFluent(IDrmSessionManagerProvider? drmSessionManagerProvider) + { + SetDrmSessionManagerProvider(drmSessionManagerProvider); + return this; + } + + public Factory SetLoadErrorHandlingPolicyFluent(ILoadErrorHandlingPolicy? loadErrorHandlingPolicy) + { + SetLoadErrorHandlingPolicy(loadErrorHandlingPolicy); + return this; + } + } + } + + public static class DashMediaSourceFactoryExtensions { - // Explicit interface implementations returning the interface type for fluent chaining - IMediaSourceMediaSourceIFactory? IMediaSourceMediaSourceIFactory.SetDrmSessionManagerProvider(IDrmSessionManagerProvider? drmSessionManagerProvider) - => SetDrmSessionManagerProvider(drmSessionManagerProvider); + /// + /// Sets the DRM session manager provider and returns the Factory for fluent chaining. + /// + public static DashMediaSource.Factory SetDrmSessionManagerProviderChained(this DashMediaSource.Factory factory, IDrmSessionManagerProvider? provider) + { + factory.SetDrmSessionManagerProvider(provider); + return factory; + } - IMediaSourceMediaSourceIFactory? IMediaSourceMediaSourceIFactory.SetLoadErrorHandlingPolicy(ILoadErrorHandlingPolicy? loadErrorHandlingPolicy) - => SetLoadErrorHandlingPolicy(loadErrorHandlingPolicy); + /// + /// Sets the load error handling policy and returns the Factory for fluent chaining. + /// + public static DashMediaSource.Factory SetLoadErrorHandlingPolicyChained(this DashMediaSource.Factory factory, ILoadErrorHandlingPolicy? policy) + { + factory.SetLoadErrorHandlingPolicy(policy); + return factory; + } } } diff --git a/source/androidx.media3/media3-exoplayer-dash/Additions/DashMediaSourceFactoryExtensions.cs b/source/androidx.media3/media3-exoplayer-dash/Additions/DashMediaSourceFactoryExtensions.cs deleted file mode 100644 index 2ca5d3642..000000000 --- a/source/androidx.media3/media3-exoplayer-dash/Additions/DashMediaSourceFactoryExtensions.cs +++ /dev/null @@ -1,39 +0,0 @@ -// Extensions to improve usability of DashMediaSource.Factory in .NET bindings. -// Provides fluent-style chaining methods that return the concrete Factory type instead of the -// generic IMediaSourceMediaSourceIFactory interface, which is the Java-exposed return type. -// This lets callers write: new DashMediaSource.Factory(ds).SetDrmSessionManagerProviderChained(...).SetLoadErrorHandlingPolicyChained(...); - -using AndroidX.Media3.ExoPlayer.Dash; -using AndroidX.Media3.ExoPlayer.Drm; -using AndroidX.Media3.ExoPlayer.Upstream; - -namespace AndroidX.Media3.ExoPlayer.Dash -{ - /// - /// Extension helpers for to enable fluent chaining - /// with strongly-typed return values. The original Java API returns a MediaSource.Factory - /// interface causing the bound method to return IMediaSourceMediaSourceIFactory. - /// These helpers wrap those calls and return the concrete . - /// - public static class DashMediaSourceFactoryExtensions - { - /// - /// Sets the DRM session manager provider and returns the same instance for chaining. - /// - public static DashMediaSource.Factory SetDrmSessionManagerProviderChained(this DashMediaSource.Factory factory, IDrmSessionManagerProvider? provider) - { - // Call the original bound method (returns the interface) and ignore its return to preserve fluent API. - factory.SetDrmSessionManagerProvider(provider); - return factory; - } - - /// - /// Sets the load error handling policy and returns the same instance for chaining. - /// - public static DashMediaSource.Factory SetLoadErrorHandlingPolicyChained(this DashMediaSource.Factory factory, ILoadErrorHandlingPolicy? policy) - { - factory.SetLoadErrorHandlingPolicy(policy); - return factory; - } - } -} diff --git a/source/androidx.media3/media3-exoplayer-dash/PublicAPI/PublicAPI.Unshipped.txt b/source/androidx.media3/media3-exoplayer-dash/PublicAPI/PublicAPI.Unshipped.txt index a30e3813d..5cd51282f 100644 --- a/source/androidx.media3/media3-exoplayer-dash/PublicAPI/PublicAPI.Unshipped.txt +++ b/source/androidx.media3/media3-exoplayer-dash/PublicAPI/PublicAPI.Unshipped.txt @@ -20,8 +20,10 @@ AndroidX.Media3.ExoPlayer.Dash.DashMediaSource.Factory.GetSupportedTypes() -> in AndroidX.Media3.ExoPlayer.Dash.DashMediaSource.Factory.SetCmcdConfigurationFactory(AndroidX.Media3.ExoPlayer.Upstream.CmcdConfiguration.IFactory? cmcdConfigurationFactory) -> AndroidX.Media3.ExoPlayer.Dash.DashMediaSource.Factory? AndroidX.Media3.ExoPlayer.Dash.DashMediaSource.Factory.SetCompositeSequenceableLoaderFactory(AndroidX.Media3.ExoPlayer.Source.ICompositeSequenceableLoaderFactory? compositeSequenceableLoaderFactory) -> AndroidX.Media3.ExoPlayer.Dash.DashMediaSource.Factory? AndroidX.Media3.ExoPlayer.Dash.DashMediaSource.Factory.SetDrmSessionManagerProvider(AndroidX.Media3.ExoPlayer.Drm.IDrmSessionManagerProvider? drmSessionManagerProvider) -> AndroidX.Media3.ExoPlayer.Source.IMediaSourceMediaSourceIFactory? +AndroidX.Media3.ExoPlayer.Dash.DashMediaSource.Factory.SetDrmSessionManagerProviderFluent(AndroidX.Media3.ExoPlayer.Drm.IDrmSessionManagerProvider? drmSessionManagerProvider) -> AndroidX.Media3.ExoPlayer.Dash.DashMediaSource.Factory! AndroidX.Media3.ExoPlayer.Dash.DashMediaSource.Factory.SetFallbackTargetLiveOffsetMs(long fallbackTargetLiveOffsetMs) -> AndroidX.Media3.ExoPlayer.Dash.DashMediaSource.Factory? AndroidX.Media3.ExoPlayer.Dash.DashMediaSource.Factory.SetLoadErrorHandlingPolicy(AndroidX.Media3.ExoPlayer.Upstream.ILoadErrorHandlingPolicy? loadErrorHandlingPolicy) -> AndroidX.Media3.ExoPlayer.Source.IMediaSourceMediaSourceIFactory? +AndroidX.Media3.ExoPlayer.Dash.DashMediaSource.Factory.SetLoadErrorHandlingPolicyFluent(AndroidX.Media3.ExoPlayer.Upstream.ILoadErrorHandlingPolicy? loadErrorHandlingPolicy) -> AndroidX.Media3.ExoPlayer.Dash.DashMediaSource.Factory! AndroidX.Media3.ExoPlayer.Dash.DashMediaSource.Factory.SetManifestParser(AndroidX.Media3.ExoPlayer.Upstream.ParsingLoadable.IParser? manifestParser) -> AndroidX.Media3.ExoPlayer.Dash.DashMediaSource.Factory? AndroidX.Media3.ExoPlayer.Dash.DashMediaSource.Factory.SetMinLiveStartPositionUs(long minLiveStartPositionUs) -> AndroidX.Media3.ExoPlayer.Dash.DashMediaSource.Factory? AndroidX.Media3.ExoPlayer.Dash.DashMediaSource.Factory.SetSubtitleParserFactory(AndroidX.Media3.Extractor.Text.ISubtitleParserFactory? subtitleParserFactory) -> AndroidX.Media3.ExoPlayer.Dash.DashMediaSource.Factory? diff --git a/source/androidx.media3/media3-exoplayer-dash/Transforms/Metadata.xml b/source/androidx.media3/media3-exoplayer-dash/Transforms/Metadata.xml index dbf52818f..a4e59bca1 100644 --- a/source/androidx.media3/media3-exoplayer-dash/Transforms/Metadata.xml +++ b/source/androidx.media3/media3-exoplayer-dash/Transforms/Metadata.xml @@ -1,5 +1,17 @@  + + AndroidX.Media3.ExoPlayer.Source.IMediaSourceMediaSourceIFactory + + + AndroidX.Media3.ExoPlayer.Source.IMediaSourceMediaSourceIFactory + AndroidX.Media3.ExoPlayer.Source.IMediaSource - - AndroidX.Media3.ExoPlayer.Source.IMediaSourceMediaSourceIFactory - - - AndroidX.Media3.ExoPlayer.Source.IMediaSourceMediaSourceIFactory - \ No newline at end of file From 487d9cb8ed8b24f1bf806082c261d3390b1f17f0 Mon Sep 17 00:00:00 2001 From: James Crutchley Date: Thu, 14 Aug 2025 15:34:09 -0700 Subject: [PATCH 4/4] [remove] Remove GetGroup method from TrackGroupArray for consistency with idiomatic C# access --- .../media3-exoplayer/Additions/TrackGroupArray.cs | 2 -- .../media3-exoplayer/PublicAPI/PublicAPI.Unshipped.txt | 1 - 2 files changed, 3 deletions(-) diff --git a/source/androidx.media3/media3-exoplayer/Additions/TrackGroupArray.cs b/source/androidx.media3/media3-exoplayer/Additions/TrackGroupArray.cs index 0e5f44c68..d184d80b0 100644 --- a/source/androidx.media3/media3-exoplayer/Additions/TrackGroupArray.cs +++ b/source/androidx.media3/media3-exoplayer/Additions/TrackGroupArray.cs @@ -6,8 +6,6 @@ namespace AndroidX.Media3.ExoPlayer.Source; // and the intuitive GetGroup(...) naming some developers attempt to use. public partial class TrackGroupArray { - // Alias matching the user's attempted call tracks.GetGroup(i) - public TrackGroup? GetGroup(int index) => Get(index); // Indexer for more idiomatic C# access: trackGroups[i] public TrackGroup? this[int index] => Get(index); diff --git a/source/androidx.media3/media3-exoplayer/PublicAPI/PublicAPI.Unshipped.txt b/source/androidx.media3/media3-exoplayer/PublicAPI/PublicAPI.Unshipped.txt index b911b776a..a2c1575fa 100644 --- a/source/androidx.media3/media3-exoplayer/PublicAPI/PublicAPI.Unshipped.txt +++ b/source/androidx.media3/media3-exoplayer/PublicAPI/PublicAPI.Unshipped.txt @@ -3446,7 +3446,6 @@ AndroidX.Media3.ExoPlayer.Source.TimelineWithUpdatedMediaItem AndroidX.Media3.ExoPlayer.Source.TimelineWithUpdatedMediaItem.TimelineWithUpdatedMediaItem(AndroidX.Media3.Common.Timeline? timeline, AndroidX.Media3.Common.MediaItem? mediaItem) -> void AndroidX.Media3.ExoPlayer.Source.TrackGroupArray AndroidX.Media3.ExoPlayer.Source.TrackGroupArray.Get(int index) -> AndroidX.Media3.Common.TrackGroup? -AndroidX.Media3.ExoPlayer.Source.TrackGroupArray.GetGroup(int index) -> AndroidX.Media3.Common.TrackGroup? AndroidX.Media3.ExoPlayer.Source.TrackGroupArray.IndexOf(AndroidX.Media3.Common.TrackGroup? group) -> int AndroidX.Media3.ExoPlayer.Source.TrackGroupArray.IsEmpty.get -> bool AndroidX.Media3.ExoPlayer.Source.TrackGroupArray.Length.get -> int