I would like to know how to be able to play more than two songs back to back in ExoPlayer using MediaSources I have in an ArrayList.
I can use ConcatenatingMediaSource to be able to play two songs back to back, but I have to load them into this fundtion as separate paramters. I do not want to do this for a whole list of songs. I have tried to find an answer to this and seem to have some fundemental misunderstanding as I can't seem to replicate the efforts of others in other StackOverflow questions or blogs etc. (Many blogs show the simple two media source playlist as in the ExoPlayer docs).
This code is for context:
private fun prepareExoPlayer(songs: ListSongs) {
val uris = parseUris(songs)
val mediaSource = buildMediaSource(uris)
applyAudioAttributes()
simpleExoPlayer!!.prepare(mediaSource, false, false)
}
This code is where the issue is:
private fun buildMediaSource(uris: ArrayList<Uri>): MediaSource {
val userAgent = Util.getUserAgent(this, "MusicPlayer")
val defaultMediaSource = DefaultDataSourceFactory(this, userAgent)
val progressiveMediaSource = ProgressiveMediaSource.Factory(defaultMediaSource)
val mediaSources = ArrayList<MediaSource>()
for (uri in uris) {
mediaSources.add(progressiveMediaSource.createMediaSource(uri))
}
return if (mediaSources.size == 1) {
mediaSources[0]
} else {
val concatenatingMediaSource = ConcatenatingMediaSource()
concatenatingMediaSource.addMediaSources(mediaSources)
// ConcatenatingMediaSource(mediaSources[0], mediaSources[1])
}
}
In the else statement I get a failure as the return type is not a MediaSource, but a Unit. However, the commented code on the last line works fine. How do I modify the 2nd and 3rd last line to be able to play my list of songs?
Ok, so I just found this video: https://www.youtube.com/watch?v=svdq1BWl4r8
Turns out prepare for ExoPlayer does not have to have a MediaSource as a parameter, but can have a ConcatenatingMediaSource as a parameter as well. These aren't the same but are both accepted by the prepare function.
It's also worth noting that ConcatenatingMediaSource can recieve a single MediaSource. This means the if statement check on the size of the MediaSource ArrayList isn't necessary.
The solution is therefore to change the return type of buildMediaSource to ConcatenatingMediaSource and remove the if statement. Like this:
private fun buildMediaSource(uris: ArrayList<Uri>): ConcatenatingMediaSource {
val userAgent = Util.getUserAgent(this, "MusicPlayer")
val defaultMediaSource = DefaultDataSourceFactory(this, userAgent)
val progressiveMediaSource = ProgressiveMediaSource.Factory(defaultMediaSource)
val mediaSources = ArrayList<MediaSource>()
for (uri in uris) {
mediaSources.add(progressiveMediaSource.createMediaSource(uri))
}
val concatenatingMediaSource = ConcatenatingMediaSource()
concatenatingMediaSource.addMediaSources(mediaSources)
return concatenatingMediaSource
}
Related
In my application I want use exoplayer to play video and for this I added this dependency:
implementation 'com.google.android.exoplayer:exoplayer:2.18.0'
with simple way I can play video with this code:
player = ExoPlayer.Builder(this).build()
binding.player.player = player
// Build the media item.
val mediaItem: MediaItem = MediaItem.fromUri(url)
// Set the media item to be played.
player.setMediaItem(mediaItem)
// Prepare the player.
player.prepare()
// Start the playback.
player.play()
But I want add Header for video and for this I write below codes:
val dataSourceFactory: DataSource.Factory = DataSource.Factory {
val dataSource: HttpDataSource = httpDataSourceFactory.createDataSource()
// Set a custom authentication request header.
dataSource.setRequestProperty("Header", "Value")
dataSource
}
But in my code not found httpDataSourceFactory.
I used this link tutorial : https://exoplayer.dev/customization.html#customizing-server-interactions
How can I add header for exoplayer?
val dataSourceFactory = DefaultHttpDataSource.Factory()
.setDefaultRequestProperties(hashMapOf("Header" to "Value"))
val audioSource: MediaSource = ProgressiveMediaSource.Factory(dataSourceFactory)
Use the "DefaultHttpDataSource"
I am using ConcatenatingMediaSource to make hls urls playlist for my ExoPlayer.
when playing media sources by local files, it works fine without gap.
However, when playing media sources by hls urls, It noticeably shows gap while transition (first video to second one)
I want my media source transit smoothly in ConcatenatingMediaSource.
How can I achieve this?
Guide me please.
This below is my init code.
val playerView = findViewById<PlayerView>(R.id.playerView)
val concatenatedSource = ConcatenatingMediaSource()
val trackSelector = DefaultTrackSelector(mContext!!)
trackSelector.parameters = trackSelector.buildUponParameters().build()
val dataSourceFactory = DefaultHttpDataSource.Factory()
val hlsMediaSource: HlsMediaSource = HlsMediaSource.Factory(dataSourceFactory)
.setAllowChunklessPreparation(true)
.createMediaSource(MediaItem.fromUri("https://multiplatform-f.akamaihd.net/i/multi/april11/sintel/sintel-hd_,512x288_450_b,640x360_700_b,768x432_1000_b,1024x576_1400_m,.mp4.csmil/master.m3u8"))
concatenatedSource.addMediaSource(hlsMediaSource)
val player = ExoPlayer.Builder(mContext)
.setTrackSelector(trackSelector)
.build()
player.setMediaSource(concatenatedSource)
player.addAnalyticsListener(EventLogger(trackSelector))
player.addListener(object : Player.Listener {
override fun onPlayerError(error: PlaybackException) {
super.onPlayerError(error)
}
})
playerView?.player = player
playerView?.player?.prepare()
playerView?.player?.playWhenReady = true
For those who have this kind of issues, you should try CacheDataSource while building MediaItem objects. This resolved my issues. Thanks
So I am trying to show subtitles from my .srt file in exoplayer but it does not work.
Do I need to use a seperate SubtitleView to get my subtitles to show up?
Is the subtitleView in the PlayerView is not enough?
I use PlayerView btw.
The exoplayer version I use is 2.14.0.
The addTextOutput method.
simpleExoPlayer.addTextOutput(cues -> {
playerView.getSubtitleView().setCues(cues);
playerView.getSubtitleView().setVisibility(View.VISIBLE);
playerView.getSubtitleView().onCues(cues);
assert cues.get(0).text != null;
Log.d("subtitles", cues.get(0).text.toString());
});
I also tried to implement to TextOutput but that did not work too.
The contents of my sample.srt file:
1
00:00:00,000 --> 00:00:01,500
For www.forom.com
2
00:00:01,500 --> 00:00:02,500
<i>Tonight's the night.</i>
3
00:00:03,000 --> 00:00:15,000
<i>And it's going to happen
again and again --</i>
The function I use to load my subtitles:
public void buildMediaSourceV3(Uri uri){
String subtitlesUri = sharedPreferencesSubtitles.getString(videoName.getText().toString(), "");
DataSource.Factory factory = new DefaultDataSourceFactory(VideoPlayer.this, getPackageName(), new DefaultBandwidthMeter());
MediaItem mediaItem = MediaItem.fromUri(uri);
MediaSource videoSource = new ProgressiveMediaSource.Factory(factory).createMediaSource(mediaItem);
if(subtitlesUri.equals(""))
{
simpleExoPlayer.addMediaSource(videoSource);
}
else
{
MediaItem.Subtitle subtitle = new MediaItem.Subtitle(Uri.parse(subtitlesUri), MimeTypes.APPLICATION_SUBRIP, "en");
MediaSource textMediaSource = new SingleSampleMediaSource.Factory(factory).createMediaSource(subtitle, C.TIME_UNSET);
textMediaSource.getMediaItem().mediaMetadata.subtitle.toString();
MergingMediaSource mergingMediaSource = new MergingMediaSource(videoSource, textMediaSource);
simpleExoPlayer.addMediaSource(mergingMediaSource);
}
}
I know you've already implemented a workaround.
I've just encountered the same issue, and found a solution:
trackSelector.setPreferredTextLanguage
This helped me. Full context:
val trackSelector = DefaultTrackSelector(context)
trackSelector.setParameters(trackSelector.buildUponParameters().setPreferredTextLanguage("en"))
Update:
I was wrong. That solution shows only video embedded captions, not the sideloading ones.
This works for me now:
val mediaItemBuilder = MediaItem.Builder()
.setUri(uri))
videoCaption?.vtt?.let {
val uriSubtitle = Uri.parse(it)
val mediaItemSubtitle = MediaItem.Subtitle(uriSubtitle,
MimeTypes.TEXT_VTT,
"en",
C.SELECTION_FLAG_DEFAULT)
mediaItemBuilder.setSubtitles(mutableListOf(mediaItemSubtitle))
}
val mediaItem = mediaItemBuilder.build()
val mediaSource = DefaultMediaSourceFactory(dataSourceFactory, extractoryFactory).createMediaSource(mediaItem)
Here is minimal example for video/mp4 with SRT subtitles. For XML layout is used com.google.android.exoplayer2.ui.StyledPlayerView and there is no need for additional subtitle layout. Files are located in assets directory inside the project.
val playerView = view.findViewById<StyledPlayerView>(R.id.video_pv)
val exoPlayer = ExoPlayer.Builder(requireActivity()).build()
playerView.player = exoPlayer
val assetSrtUri = Uri.parse(("file:///android_asset/subtitle.srt"))
val subtitle = SubtitleConfiguration.Builder(assetSrtUri)
.setMimeType(MimeTypes.APPLICATION_SUBRIP)
.setLanguage("en")
.setSelectionFlags(C.SELECTION_FLAG_DEFAULT)
.build()
val assetVideoUri = Uri.parse(("file:///android_asset/video.mp4"))
val mediaItem = MediaItem.Builder()
.setUri(assetVideoUri)
.setSubtitleConfigurations(ImmutableList.of(subtitle))
.build()
exoPlayer.setMediaItem(mediaItem)
exoPlayer.prepare()
exoPlayer.play()
Gradle dependencies:
implementation "com.google.android.exoplayer:exoplayer:2.17.1"
implementation "com.google.android.exoplayer:exoplayer-core:2.17.1"
implementation "com.google.android.exoplayer:exoplayer-ui:2.17.1"
In my app the user can select some videos from the gallery to edit them. After the user selects videos, I create a list of Uri and I want to play in exoplayer. I can play videos using their file path such as /storage/emulated/0/Android/data/com.vitor.videoeditor/files/Movies/VideoEditor/VID_20200722_133503.mp4, but I don't know how to do the same with a uri like content: // media / external / file / 305166.I tried to pass the uri directly to DataSpec () but it didn't work. What is the correct way to play the videos?
private fun playVideos(pathsList: List<String>) {
val concatenatingMediaSource = ConcatenatingMediaSource()
pathsList.forEach { filePath ->
val dataSpec = DataSpec(Uri.fromFile(File(filePath)))
val fileDataSource = FileDataSource()
try {
fileDataSource.open(dataSpec)
} catch (e: FileDataSource.FileDataSourceException) {
e.printStackTrace()
}
val dataSourceFactory = DataSource.Factory { fileDataSource }
val mediaSource: MediaSource = ProgressiveMediaSource.Factory(dataSourceFactory)
.createMediaSource(Uri.fromFile(File(filePath)))
concatenatingMediaSource.addMediaSource(mediaSource)
}
simpleExoPlayer = ExoPlayerFactory.newSimpleInstance(applicationContext)
with(simpleExoPlayer) {
prepare(concatenatingMediaSource)
playerView.player = simpleExoPlayer
playWhenReady = true
repeatMode = Player.REPEAT_MODE_ALL
}
}
content uri will expires soon So only during the file opening by intent you can play a video then it will expire. to avoid that
val cr = applicationContext.contentResolver
val flg: Int = Intent.FLAG_GRANT_WRITE_URI_PERMISSION or
Intent.FLAG_GRANT_READ_URI_PERMISSION
cr.takePersistableUriPermission(uri!!, flg)
put above code in onActivity result. So that the uri will not expire.
and use same content uri later on.
I have a class activity_player layout in which I have exoplayer2.ui.PlayerView and I created exo_player_control_view so that it overrides default controls in ExoPlayer. So I wanted to use Databinding in newly created custom control view but don't know how to do it. Any advice?
It is actually an open issue over here, but yet to be solved. So is there anyone who had a workaround to make exo_player_control_view Databinding friendly?
Use Like this >>>>>
private val binding by lazy {
ActivityPipVideoPlayerBinding.inflate(layoutInflater)}
private val exoPLayerBinding by lazy {
VdoExoControlViewBinding.inflate(LayoutInflater.from(this), binding.root, true)
}
You can use binding variable inside fragment/activity to access the playerView inside fragment/activity and
val uri: Uri? = if (url is String) Uri.parse(url as String?) else url as Uri?
val trackSelector =
DefaultTrackSelector(AdaptiveTrackSelection.Factory(DefaultBandwidthMeter()))
val player: SimpleExoPlayer = ExoPlayerFactory.newSimpleInstance(view.context, trackSelector)
val dataSourceFactory = DefaultDataSourceFactory(view.context, "ua")
val mediaSource =
ExtractorMediaSource(uri, dataSourceFactory, DefaultExtractorsFactory(), null, null)
player.prepare(mediaSource)
player.apply {
volume = 0f
repeatMode = Player.REPEAT_MODE_ONE
playWhenReady = true
videoScalingMode = C.VIDEO_SCALING_MODE_SCALE_TO_FIT
}
binding.playerView.setResizeMode(AspectRatioFrameLayout.RESIZE_MODE_FILL)
binding.playerView.player = player