Skip to content

Conversation

@nateshmbhat
Copy link
Contributor

@nateshmbhat nateshmbhat commented Aug 29, 2025

Description

This PR adds comprehensive audio track retrieval and selection support to the video_player package, enabling developers to access detailed information about available audio tracks and switch between them during playback.

Changes Made

Core Features

  • Added VideoAudioTrack model with comprehensive metadata fields: id, label, language, isSelected, bitrate, sampleRate, channelCount, codec
  • Added getAudioTracks() method to retrieve all available audio tracks with real metadata
  • Added selectAudioTrack() method to switch between audio tracks during playback
  • Updated VideoPlayerController to expose audio track functionality

Platform Implementations

  • Android:
    • Real metadata extraction using ExoPlayer's getCurrentTracks() API
    • Robust track selection using TrackSelectionOverride with proper error handling
    • Support for multiple audio formats (AAC, AC3, EAC3, MP3, etc.)
  • iOS:
    • Metadata extraction from AVFoundation using AVAssetTrack for regular videos
    • HLS stream support using AVMediaSelectionGroup for adaptive streams
    • Proper track selection for both asset tracks and media selection options

Technical Infrastructure

  • Updated Pigeon interfaces for both Android and iOS with new data structures:
    • AudioTrackMessage, ExoPlayerAudioTrackData, AssetAudioTrackData, MediaSelectionAudioTrackData, NativeAudioTrackData
  • Enhanced platform interface with new methods and data classes
  • Has native unit tests for both Android and iOS platforms
  • Created demo screen showcasing audio track functionality with interactive UI

Demo Features

  • Interactive video player with audio track selection
  • Real-time metadata display (bitrate, sample rate, channels, codec)
  • Support for multiple video sources including HLS streams
  • Visual indicators for currently selected tracks

Related Issues

#59437 - Add audio track metadata support to video_player

Testing

  • Added native unit tests for both Android and iOS
  • Tested with various video formats and HLS streams
  • Verified backward compatibility with existing functionality
  • Demo screen widget for manual testing and validation

Breaking Changes

None - all changes are additive and backward compatible.

Platform PRs here :

  1. feat(android): add audio track selection for ExoPlayer #10312
  2. feat(video): add audio track selection support for AVFoundation #10313

Pre-Review Checklist

  • I read the [Contributor Guide] and followed the process outlined there for submitting PRs.
  • I read the [Tree Hygiene] page, which explains my responsibilities.
  • I read and followed the [relevant style guides] and ran [the auto-formatter].
  • I signed the [CLA].
  • The title of the PR starts with the name of the package surrounded by square brackets, e.g. [video_player]
  • I [linked to at least one issue that this PR fixes] in the description above.
  • I updated pubspec.yaml with an appropriate new version according to the [pub versioning philosophy], or I have commented below to indicate which [version change exemption] this PR falls under[^1].
  • I updated CHANGELOG.md to add a description of the change, [following repository CHANGELOG style], or I have commented below to indicate which [CHANGELOG exemption] this PR falls under[^1].
  • I updated/added any relevant documentation (doc comments with ///).
  • I added new tests to check the change I am making, or I have commented below to indicate which [test exemption] this PR falls under[^1].
  • All existing and new tests are passing.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a valuable feature for fetching and selecting audio tracks in the video_player package. The changes are comprehensive, touching the platform interface, Android (ExoPlayer) and iOS (AVFoundation) implementations, and adding a new demo screen.

My review has identified several critical and high-severity issues that should be addressed:

  • The new native unit tests for both Android and iOS appear to be broken due to type mismatches and incorrect method calls, and will not compile in their current state.
  • The Dart implementation code for both Android and iOS contains unsafe null assertions on data coming from the native side, which could lead to runtime crashes.
  • The Android implementation has been updated to use a milestone version of Gradle, which is not recommended for production packages.
  • The new audio track selection feature has not been implemented for the web platform, which will lead to UnimplementedError on web.
  • There is a minor issue in the new example app where context is used after an await without checking if the widget is still mounted.

Addressing these points will significantly improve the robustness and completeness of this new feature.

@nateshmbhat
Copy link
Contributor Author

I updated native andorid tests and ran the tests and verified that all native android tests are passing.

@nateshmbhat
Copy link
Contributor Author

@stuartmorgan-g @ash2moon can you please comment on this PR and lemme know if there are any concerns ?

@stuartmorgan-g stuartmorgan-g added triage-ios Should be looked at in iOS triage triage-android Should be looked at in Android triage labels Sep 2, 2025
auto-submit bot pushed a commit that referenced this pull request Oct 27, 2025
…ce (#10171)

## Description
platform interface pr for #9925 

#### Core Features
- **Added `VideoAudioTrack` model** with comprehensive metadata fields: `id`, `label`, `language`, `isSelected`, `bitrate`, `sampleRate`, `channelCount`, `codec`
- **Added [getAudioTracks()]() method** to retrieve all available audio tracks with real metadata
- **Added [selectAudioTrack()]() method** to switch between audio tracks during playback

### Breaking Changes
None - all changes are additive and backward compatible.

## Pre-Review Checklist
@nateshmbhat
Copy link
Contributor Author

Any update on this PR needed from my side?

@stuartmorgan-g
Copy link
Collaborator

@nateshmbhat You can split out a new PR with the platform implementations (and with the overrides removed in favor of requiring the newly published interface package version), and then the rest of the platform implementation reviews can happen there.


// Sample video URLs with multiple audio tracks
final List<String> _sampleVideos = <String>[
'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Example:

_controller = MiniController.network(
'https://flutter.github.io/assets-for-api-docs/assets/videos/bee.mp4',
viewType: widget.viewType,


// Sample video URLs with multiple audio tracks
final List<String> _sampleVideos = <String>[
'https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or you can upload one with a proper license to https://github.com/flutter/assets-for-api-docs/tree/main/assets/videos

await controller.initialize();

// Add listener for video player state changes
_controller!.addListener(_onVideoPlayerValueChanged);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: here and below, use the local controller to avoid !.

}

void _onVideoPlayerValueChanged() {
if (!mounted || _controller == null) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

uber nit: the mounted check is not needed if this method is unsubscribed at unmount.

return const Center(child: CircularProgressIndicator());
}

if (_error != null) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (_error != null) {
if (_error case final String error?) {

so you don't need to use !.

if (!kIsWeb && Platform.isAndroid) {
// Add a small delay to allow ExoPlayer to process the track selection change
// This is needed because ExoPlayer's track selection update is asynchronous
await Future<void>.delayed(const Duration(milliseconds: 100));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is still scary even with thorough testing. A future version of exoPlayer can break this hardcoded delay timing (in case the selection takes longer to complete in that version).

I'm not familiar with the API of exoPlayer, but not having an API to tell whether the audio track selection is completed just feels a bit strange.

NSString *displayName = option.displayName;

NSString *languageCode = nil;
if (option.locale) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: the if doesn't seem to be necessary?

return [FVPNativeAudioTrackData makeWithAssetTracks:nil mediaSelectionTracks:nil];
}

AVAsset *asset = currentItem.asset;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The documentation says this returns an empty array when tracks haven't been loaded yet. Can this method be called when the tracks are not yet loaded?

// Only process objects that are clearly Core Media format descriptions
// This works for both real CMFormatDescription objects and properly configured mock
// objects
if ([className hasPrefix:@"CMAudioFormatDescription"] ||
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we should include logic that pertains to testing / mocking.

[asset mediaSelectionGroupForMediaCharacteristic:AVMediaCharacteristicAudible];
if (audioGroup && audioGroup.options.count > 0) {
// Parse the track ID to get the index
NSString *indexString = [trackId substringFromIndex:[@"media_selection_" length]];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks very fragile. Could you find an alternative to map trackID to the track?

@nateshmbhat
Copy link
Contributor Author

nateshmbhat commented Oct 28, 2025

@nateshmbhat You can split out a new PR with the platform implementations (and with the overrides removed in favor of requiring the newly published interface package version), and then the rest of the platform implementation reviews can happen there.

@stuartmorgan-g Raised #10313 and #10312

- Added new data classes for audio track management (PlaybackState, AudioTrackMessage, ExoPlayerAudioTrackData, NativeAudioTrackData)
- Implemented getAudioTracks() and selectAudioTrack() methods in VideoPlayerInstanceApi
- Fixed Map comparison in deepEquals to use containsKey instead of contains
- Added message codec handling for new audio track data classes
- Updated Android video player implementation to support audio track selection
- Updated video_player_platform_interface dependency from ^6.3.0 to ^6.6.0 across all video_player packages
- Removed temporary dependency_overrides for video_player_platform_interface that were used for testing
- Reformatted dependency override paths for better readability in remaining overrides
- Reduced sample video URLs from 3 to 2 by removing redundant examples
- Changed primary video source to flutter.github.io butterfly video for better reliability
- Simplified audio track data construction in Android native code by using direct constructor instead of builder pattern
- Fixed code formatting and line wrapping in audio tracks demo UI
- Removed unnecessary builder pattern usage in NativeAudioTrackData creation
- Removed fully qualified class names (Messages.ExoPlayerAudioTrackData) in favor of direct class names for better readability
- Updated getter method name from getIsSelected() to isSelected() to follow Java boolean getter conventions
- Maintained same test coverage and assertions while making code more concise
- No functional changes to the test behavior or validation logic
- Upgraded video_player_platform_interface dependency from ^6.4.0 to ^6.6.0 in video_player_web package
- Maintains compatibility with web platform support while incorporating latest interface updates
…he code formatting changes:

style(video_player): improve code formatting and line wrapping

- Fixed line wrapping and indentation in audio_tracks_demo.dart for better readability
- Simplified import statements and removed fully qualified class names in AudioTracksTest.java
- Improved code formatting and line breaks in avfoundation_video_player files
- Adjusted indentation and line breaks in example files to follow consistent style
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants