Skip to content
Closed
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
c331d42
feat(video_player): add audio track metadata support with bitrate, sa…
nateshmbhat Aug 11, 2025
870b702
feat(video): implement audio track retrieval for HLS streams in iOS
nateshmbhat Aug 11, 2025
3435c20
test(video_player): update test cases
nateshmbhat Aug 11, 2025
4deae93
refactor(video_player): move audio track formatting from model to UI …
nateshmbhat Aug 11, 2025
86ba273
chore(video_player): updated pubspec
nateshmbhat Aug 11, 2025
5e09464
Merge branch 'main' into main
nateshmbhat Aug 11, 2025
6bab30b
feat(video_player): add audio track retrieval functionality and tests
nateshmbhat Aug 11, 2025
2dec18d
Merge branch 'main' of https://github.com/flutter/packages
nateshmbhat Aug 11, 2025
d29ad6c
Merge branch 'main' of github.com:nateshmbhat/flutter_packages
nateshmbhat Aug 11, 2025
cbb854b
feat(video_player): updated dependencies in pubspec to path based on …
nateshmbhat Aug 12, 2025
c7488c1
Merge branch 'main' into main
nateshmbhat Aug 12, 2025
567925d
fix(video_player): fix code for failing ci/cd checks
nateshmbhat Aug 12, 2025
a3a0884
Merge branch 'main' of github.com:nateshmbhat/flutter_packages
nateshmbhat Aug 12, 2025
1dfbf82
fix(video_player_web): reverted changes in video_player_web
nateshmbhat Aug 12, 2025
638bf43
feat(video_player): ios code refactor and added tests
nateshmbhat Aug 13, 2025
e507002
fix: updated ios tests
nateshmbhat Aug 13, 2025
d7aa9fb
fix(ios): updated method name for getAudioTracks
nateshmbhat Aug 13, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions packages/video_player/video_player/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 2.11.0

* Adds audio track metadata support including bitrate, sample rate, channel count, and codec information.

## 2.10.0

* Adds support for platform views as an optional way of displaying a video on Android and iOS.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@
97C146EC1CF9000F007C117D /* Resources */,
9705A1C41CF9048500538489 /* Embed Frameworks */,
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
A3677D96C5C9245FC9DDA03F /* [CP] Embed Pods Frameworks */,
);
buildRules = (
);
Expand Down Expand Up @@ -236,6 +237,23 @@
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
};
A3677D96C5C9245FC9DDA03F /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Embed Pods Frameworks";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
A526C4C26D549003F5EB64A6 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'package:flutter/material.dart';
import 'package:video_player/video_player.dart';

/// Demo page showing how to retrieve and display available audio tracks
class AudioTracksDemo extends StatefulWidget {
/// Creates an AudioTracksDemo widget.
const AudioTracksDemo({super.key});

@override
State<AudioTracksDemo> createState() => _AudioTracksDemoState();
}

class _AudioTracksDemoState extends State<AudioTracksDemo> {
VideoPlayerController? _controller;
List<VideoAudioTrack> _audioTracks = <VideoAudioTrack>[];
bool _isLoading = false;

@override
void initState() {
super.initState();
_initializeVideoPlayer();
}

Future<void> _initializeVideoPlayer() async {
// Apple's test HLS stream with multiple audio tracks
const String videoUrl =
'https://devstreaming-cdn.apple.com/videos/streaming/examples/bipbop_16x9/bipbop_16x9_variant.m3u8';

_controller = VideoPlayerController.networkUrl(Uri.parse(videoUrl));

try {
await _controller!.initialize();
setState(() {
// Video initialized
});

// Get audio tracks after initialization
await _getAudioTracks();
} catch (e) {
debugPrint('Error initializing video player: $e');
}
}

Future<void> _getAudioTracks() async {
if (_controller == null) {
return;
}

setState(() {
_isLoading = true;
});

try {
final List<VideoAudioTrack> tracks = await _controller!.getAudioTracks();
setState(() {
_audioTracks = tracks;
_isLoading = false;
});
} catch (e) {
debugPrint('Error getting audio tracks: $e');
setState(() {
_isLoading = false;
});
}
}

@override
void dispose() {
_controller?.dispose();
super.dispose();
}

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Audio Tracks Demo'),
backgroundColor: Colors.blue,
),
body: Column(
children: <Widget>[
// Video Player
if (_controller != null && _controller!.value.isInitialized)
AspectRatio(
aspectRatio: _controller!.value.aspectRatio,
child: VideoPlayer(_controller!),
)
else
const SizedBox(
height: 200,
child: Center(
child: CircularProgressIndicator(),
),
),

// Video Controls
if (_controller != null && _controller!.value.isInitialized)
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
IconButton(
onPressed: () {
setState(() {
if (_controller!.value.isPlaying) {
_controller!.pause();
} else {
_controller!.play();
}
});
},
icon: Icon(
_controller!.value.isPlaying
? Icons.pause
: Icons.play_arrow,
),
),
IconButton(
onPressed: _getAudioTracks,
icon: const Icon(Icons.refresh),
tooltip: 'Refresh Audio Tracks',
),
],
),

const Divider(),

// Audio Tracks Section
Expanded(
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Row(
children: <Widget>[
const Text(
'Available Audio Tracks:',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
const Spacer(),
if (_isLoading)
const SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(strokeWidth: 2),
),
],
),
const SizedBox(height: 16),
if (_audioTracks.isEmpty && !_isLoading)
const Text(
'No audio tracks found or video not initialized.',
style: TextStyle(color: Colors.grey),
)
else
Expanded(
child: ListView.builder(
itemCount: _audioTracks.length,
itemBuilder: (BuildContext context, int index) {
final VideoAudioTrack track = _audioTracks[index];
return Card(
margin: const EdgeInsets.only(bottom: 8),
child: ListTile(
leading: CircleAvatar(
backgroundColor: track.isSelected
? Colors.green
: Colors.grey,
child: Icon(
track.isSelected
? Icons.check
: Icons.audiotrack,
color: Colors.white,
),
),
title: Text(
track.label,
style: TextStyle(
fontWeight: track.isSelected
? FontWeight.bold
: FontWeight.normal,
),
),
subtitle: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text('ID: ${track.id}'),
Text('Language: ${track.language}'),
],
),
trailing: track.isSelected
? const Chip(
label: Text('Selected'),
backgroundColor: Colors.green,
labelStyle:
TextStyle(color: Colors.white),
)
: null,
),
);
},
),
),
],
),
),
),
],
),
);
}
}
Loading