How do I switch to a specific track by index in Exoplayer? - android

I am creating a music player using Exoplayer. If the user clicks on a specific track from recyclerview I want to play all tracks but switch to clicked index.
I am using ConcatenatingMediaSource to play the playlist.
val concatenatedSource = ConcatenatingMediaSource()
for (v in videos) {
when (v.type) {
"join" -> {
val mediaSource = ProgressiveMediaSource.Factory(cacheDataSourceFactory)
.createMediaSource(Uri.parse(v.src))
concatenatedSource.addMediaSource(mediaSource)
}
"separate" -> {
val videoSource = ProgressiveMediaSource.Factory(cacheDataSourceFactory)
.createMediaSource(Uri.parse(v.vSrc))
val audioSource = ProgressiveMediaSource.Factory(cacheDataSourceFactory)
.createMediaSource(Uri.parse(v.aSrc))
val mergedSource = MergingMediaSource(videoSource, audioSource)
concatenatedSource.addMediaSource(mergedSource)
}
"stream" -> {
val hlsSource = HlsMediaSource.Factory(cacheDataSourceFactory)
.createMediaSource(Uri.parse(v.src))
concatenatedSource.addMediaSource(hlsSource)
}
}
}
player.prepare(concatenatedSource)

Related

How to play encrypted video in exoplayer (android app) coming from HLS streamer?

I am right now using FFMPEG to stream mp4 file using HLS.
I am using this link to enable encryption: https://hlsbook.net/how-to-encrypt-hls-video-with-ffmpeg/
To play video in my android app, I am using exoplayer, below is my source code to play video:
Player player;
private MediaSource buildMediaSource(Uri uri) {
TrackSelection.Factory adaptiveTrackSelection = new AdaptiveTrackSelection.Factory(new DefaultBandwidthMeter());
player = ExoPlayerFactory.newSimpleInstance(
this,
new DefaultTrackSelector(adaptiveTrackSelection));
playerView.setPlayer(player);
// These factories are used to construct two media sources.
DefaultBandwidthMeter defaultBandwidthMeter = DefaultBandwidthMeter.getSingletonInstance(this);
DataSource.Factory dataSourceFactory = new DefaultDataSourceFactory(mContext,
Util.getUserAgent(mContext, "cookvid"), defaultBandwidthMeter);
//DataSource.Factory dataSourceFactory =
// new DefaultDataSourceFactory(this, "exoplayer-codelab");
HlsMediaSource.Factory mediaSourceFactory = new HlsMediaSource.Factory(dataSourceFactory);
return mediaSourceFactory.createMediaSource(uri);
//return new ProgressiveMediaSource.Factory(dataSourceFactory)
// .createMediaSource(uri);
}
private void initializePlayer() {
Uri uri = Uri.parse(getString(R.string.media_url_hls));
MediaSource mediaSource = buildMediaSource(uri);
player.setPlayWhenReady(playWhenReady);
player.seekTo(currentWindow, playbackPosition);
player.addListener(playbackStateListener);
player.prepare(mediaSource, false, false);
}
But with this code, I can not play video in app, If I am not using this encryption ,then exoplayer can play video without any issue.
Please help me on this, I am newbie on exoplayer side.
I created for you an example in Kotlin to make it work:
class MainActivity : AppCompatActivity() {
private var exoPlayer: SimpleExoPlayer? = null
private var trackSelector: DefaultTrackSelector? = null
var drmSessionManager: DefaultDrmSessionManager? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
trackSelector = DefaultTrackSelector(this)
exoPlayer = SimpleExoPlayer.Builder(this)
.setTrackSelector(trackSelector!!)
.build()
player_view.player = exoPlayer
var uri = Uri.fromFile( File("//android_asset/legend_enc.mp4"))
playVideo(uri, "zX65/4jzTK6wYYWwACTkwg", "Y8tfcYTdS2iaXF/xHuajKA")
}
private fun playVideo(url: Uri, id: String, value: String){
try {
drmSessionManager =
Util.getDrmUuid(C.CLEARKEY_UUID.toString())?.let { buildDrmSessionManager(
it,
true,
id,
value
) }
} catch (e: UnsupportedDrmException) {
e.printStackTrace()
}
exoPlayer?.setMediaSource(buildDashMediaSource(url))
exoPlayer?.prepare()
exoPlayer?.playWhenReady = true
}
private fun buildDashMediaSource(uri: Uri): MediaSource {
val dashChunkSourceFactory = DefaultDataSourceFactory(this, "agent")
return ProgressiveMediaSource.Factory(dashChunkSourceFactory)
.setDrmSessionManager(drmSessionManager ?: DrmSessionManager.DUMMY)
.createMediaSource(uri)
}
#Throws(UnsupportedDrmException::class)
private fun buildDrmSessionManager(uuid: UUID, multiSession: Boolean, id: String, value: String): DefaultDrmSessionManager {
val drmCallback = LocalMediaDrmCallback("{\"keys\":[{\"kty\":\"oct\",\"k\":\"${value}\",\"kid\":\"${id}\"}],\"type\":\"temporary\"}".toByteArray())
val mediaDrm = FrameworkMediaDrm.newInstance(uuid)
return DefaultDrmSessionManager(uuid, mediaDrm, drmCallback, null, multiSession)
}
}

How to play a video on exoplayer from a content uri?

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.

what's the difference between this two video url using exoplayer in android?

here is two urls. First is "https://28api.haii.io/media/video/video_60_1.mp4",and Second is "https://28api.haii.io/media/video/video_70_1.mp4". When I load first video url, it load and play well. But when I load second url, the second video is broken. I can here only audio. So I tried comparing this two video by download.. but I can't understand the difference between this two url. Both are mp4 encoded video. But Exoplayer only load well at first but not at second.... how can I solve this... help me...
private fun initializePlayer() {
if (player == null) {
val trackSelector = DefaultTrackSelector()
trackSelector.setParameters(
trackSelector.buildUponParameters().setMaxVideoSizeSd()
)
player = ExoPlayerFactory.newSimpleInstance(context,trackSelector)
binding.videoPlayer.player = player
binding.videoPlayer.useController=false
binding.playerControl.player = binding.videoPlayer.player
mediaSource = mediaSourceFactory.createMediaSource(Uri.parse(mediaList[currentIndex].url))
player!!.prepare(mediaSource,false,false)
player!!.seekTo(currentWindow, playbackPosition)
player!!.playWhenReady = playWhenReady
setAudioFocus()
player!!.addListener(object : Player.EventListener {
override fun onPlayerStateChanged(playWhenReady: Boolean, playbackState: Int) {
when (playbackState) {
Player.STATE_IDLE -> {
}
Player.STATE_BUFFERING -> {
}
Player.STATE_READY -> {
}
Player.STATE_ENDED -> {
showPlayButton()
player!!.seekTo(currentWindow, 0)
showThumbnailImage()
player!!.playWhenReady = false
}
else -> {
}
}
}
})
}
}
private fun getVideoSource(url :String){
videoUrl = url
var mediaSource = mediaSourceFactory.createMediaSource(Uri.parse(url))
player!!.prepare(mediaSource)
playbackPosition=0
player!!.seekTo(currentWindow, playbackPosition)
player!!.playWhenReady = false
}

Exoplayer2 Streaming HLS Videos , Sometimes plays video with sound only(video not played)

I implement exoplayer2 in my app for playing HLS Videos, but sometimes it plays just sound, and doesn't work correctly. What am I supposed to do? I couldn't find why this happens sometimes.
this is the code for initializing player :
fun initPalyer(){
val mainHandler = Handler()
val bandwidthMeter: BandwidthMeter = DefaultBandwidthMeter.Builder(context).build()
bandwidthMeter.addEventListener(mainHandler!!, this)
val trackSelectionFactory: TrackSelection.Factory = AdaptiveTrackSelection.Factory()
trackSelector = DefaultTrackSelector(context, trackSelectionFactory)
val builder = ParametersBuilder(context)
trackSelectorParameters = builder.build()
trackSelector!!.parameters = trackSelectorParameters
var rendersFactory: RenderersFactory = app.buildRenderersFactory(false)
player = SimpleExoPlayer.Builder(
context, renderersFactory
)
.setTrackSelector(trackSelector!!)
.setBandwidthMeter(bandwidthMeter)
//.setLoadControl(loadControl)
.build()
player!!.addListener(this)
loadState()
playerView.player = player!!
}
the code for preparing player :
private fun preparePlayer(uri: Uri) {
val mediaSource = MediaSourceBuilder().build(uri)
durationSet = false
player?.prepare(mediaSource, true, false)
}
and the code for creating mediaSources :
class MediaSourceBuilder {
//Build various MediaSource depending upon the type of Media for a given video/audio uri
fun build(uri: Uri): MediaSource {
val userAgent = PlayerConstants.USER_AGENT
val lastPath = uri.lastPathSegment?:""
val defaultHttpDataSourceFactory = DefaultHttpDataSourceFactory(userAgent)
if(lastPath.contains(PlayerConstants.FORMAT_MP3) || lastPath.contains(PlayerConstants.FORMAT_MP4)){
return ExtractorMediaSource.Factory(defaultHttpDataSourceFactory)
.createMediaSource(uri)
}else if(lastPath.contains(PlayerConstants.FORMAT_M3U8)){
return HlsMediaSource.Factory(defaultHttpDataSourceFactory)
.setAllowChunklessPreparation(true)
.createMediaSource(uri)
}else{
val dashChunkSourceFactory = DefaultDashChunkSource.Factory(defaultHttpDataSourceFactory)
return DashMediaSource.Factory(dashChunkSourceFactory, defaultHttpDataSourceFactory)
.createMediaSource(uri)
}
}
You might wanna change your DataSourceFactory, in case your URL are in HTTPS you might end up with an error, try using DefaultDataSourceFactory instead of DefaultHttpDataSourceFactory

Android Exoplayer - Different URLs for different video quality and change manually

I am using exoplayer in my application. I am using it to play video from a url. What i am trying to do is that i have three different urls for high,medium and low quality of same the video, and i would like to let the user to be able to change the video quality manually.
{
"lowQualityUrl":"string url",
"mediumQualityUrl":"string url",
"highQualityUrl":"string url"
}
In JWplayer there is an option to add different sources/url for different qualities. Is there something similar that can be done in exoplayer...?
Edit : I don't want to play videos one after another. I just want to switch to a different quality of the same video, like in youtube. But instead of using a single url for the source, what i have are 3 different urls for 3 qualities(low,medium,high) of the same video.
I found a solution or rather a workaround for the issue. I am using the exoplayer inside a recylcerview. This code might need some optimization.
I got this idea from another answer which was under this question,which had this github link, but i think the author deleted it.
What i did was create a class for keeping all the urls for a particular video. Then showing a Spinner above the exoplayer, and when user selects a particular quality then i prepare the exoplayer with the new URL, and then seekto to the previously playing position. You can ignore the StringUtils methods.
VideoPlayerConfig.kt
object VideoPlayerConfig {
//Minimum Video you want to buffer while Playing
val MIN_BUFFER_DURATION = 3000
//Max Video you want to buffer during PlayBack
val MAX_BUFFER_DURATION = 5000
//Min Video you want to buffer before start Playing it
val MIN_PLAYBACK_START_BUFFER = 1500
//Min video You want to buffer when user resumes video
val MIN_PLAYBACK_RESUME_BUFFER = 5000
}
VideoQuality.kt
You can change this class according to your need. I need to store exactly 3 urls for low,medium and high quality urls. And i needed to show them in that order as well in the spinner.
class VideoQuality {
private val videoQualityUrls = HashMap<String, String>()
companion object {
val LOW = getStringResource(R.string.low)
val MEDIUM = getStringResource(R.string.medium)
val HIGH = getStringResource(R.string.high)
}
val qualityArray
get() = arrayListOf<String>().apply {
if (hasQuality(LOW)) add(LOW)
if (hasQuality(MEDIUM)) add(MEDIUM)
if (hasQuality(HIGH)) add(HIGH)
}
var defaultVideoQuality: String? = HIGH
var lowQuality: String?
set(value) {
setVideoQualityUrl(LOW, value)
}
get() = videoQualityUrls[LOW] ?: ""
var mediumQuality: String?
set(value) {
setVideoQualityUrl(MEDIUM, value)
}
get() = videoQualityUrls[MEDIUM] ?: ""
var highQuality: String?
set(value) {
setVideoQualityUrl(HIGH, value)
}
get() = videoQualityUrls[HIGH] ?: ""
private fun setVideoQualityUrl(quality: String?, url: String?) {
if (url != null && quality != null) {
videoQualityUrls[quality] = url
}
}
private fun hasQuality(quality: String?): Boolean {
if (videoQualityUrls[quality] == null) {
return false
}
return true
}
fun getVideoQualityUrl(quality: String?): String? {
return videoQualityUrls[quality]
}
}
Methods to implement for the exoplayer
private fun initializePlayer() {
if (exoPlayer == null) {
val loadControl = DefaultLoadControl.Builder()
.setBufferDurationsMs(2 * VideoPlayerConfig.MIN_BUFFER_DURATION, 2 * VideoPlayerConfig.MAX_BUFFER_DURATION, VideoPlayerConfig.MIN_PLAYBACK_START_BUFFER, VideoPlayerConfig.MIN_PLAYBACK_RESUME_BUFFER)
.createDefaultLoadControl()
//Create a default TrackSelector
val videoTrackSelectionFactory = AdaptiveTrackSelection.Factory()
val trackSelector = DefaultTrackSelector(videoTrackSelectionFactory)
exoPlayer = ExoPlayerFactory.newSimpleInstance(itemView.context, DefaultRenderersFactory(itemView.context), trackSelector, loadControl)
exoPlayer!!.addListener(PlayEventListener())
val videoQualityInfo:VideoQuality = videoListVideoDataHolderData!!.videoQualityUrls //Just an object that i created and stored in a dataHolder for this view.
val url = videoQualityInfo.getVideoQualityUrl(videoQualityInfo.defaultVideoQuality) ?: ""
preparePlayer(url)
}
}
private fun preparePlayer(url: String) {
if (url.isNotEmpty()) {
val mediaSource = buildMediaSource(StringUtils.makeHttpUrl(url))
exoPlayer?.prepare(mediaSource)
videoView.player = exoPlayer
} else {
Log.d(APPTAG, "NO DEFAULT URL")
}
}
private fun buildMediaSource(url: String): ProgressiveMediaSource {
val mUri: Uri = Uri.parse(url)
val dataSourceFactory = DefaultDataSourceFactory(
itemView.context,
Util.getUserAgent(itemView.context, getStringResource(R.string.app_name))
)
val videoSource = ProgressiveMediaSource.Factory(dataSourceFactory)
.createMediaSource(mUri)
return videoSource
}
And then in the Spinner/QualitySelector's OnItemSelectedListener
videoQualitySpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onNothingSelected(parent: AdapterView<*>?) {
}
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
val currentTime = exoPlayer?.currentPosition
val isReadyToPlay = exoPlayer?.playWhenReady
val urlToBuild = when (videoQualityUrls.qualityArray[position]) {
VideoQuality.LOW -> videoQualityUrls.lowQuality
VideoQuality.MEDIUM -> videoQualityUrls.mediumQuality
else -> videoQualityUrls.highQuality
}
Log.d(APPTAG, "VIDEO DETAILS :::: ${currentTime} ${isReadyToPlay} ${urlToBuild}")
if (!urlToBuild.isNullOrEmpty()) {
val mediaSource = buildMediaSource(StringUtils.makeHttpUrl(urlToBuild))
exoPlayer?.prepare(mediaSource)
exoPlayer?.playWhenReady = isReadyToPlay ?: false
exoPlayer?.seekTo(currentTime ?: 0)
}
}
}

Categories

Resources