google / ExoPlayer

An extensible media player for Android
Apache License 2.0
21.65k stars 6k forks source link

How to extract metadata from udp stream of a ts file #7799

Open danisebastio opened 3 years ago

danisebastio commented 3 years ago

Hi, i have an udp stream from which i'd like to extract metadata flow. I'm streaming a ts file from vlc media player. I can stream but i can't receive metadata. I'm using the following code to extract metadata:

 override fun onTracksChanged(
                trackGroups: TrackGroupArray,
                trackSelections: TrackSelectionArray
            ) {
                simpleExoPlayer?.addAnalyticsListener(EventLogger(trackSelector))
                for (i in 0 until trackGroups.length) {
                    val trackGroup = trackGroups.get(i)
                    for (j in 0 until trackGroup.length) {
                        val trackMetadata = trackGroup.getFormat(j)
                        if (trackMetadata != null) {
                                  //do something with metadata
                        }
                    }
                }
            }

but i don't receive nothing, i need to receive metadata like: duration, in playback title, next and previous program etc... how can i resolve this issue? Thank you!

AquilesCanta commented 3 years ago

duration

You can obtain that from Player.getDuration().

playback title next and previous program

Where is that information available in the stream? Is it PSI or an ID3 track?

danisebastio commented 3 years ago

duration

You can obtain that from Player.getDuration().

playback title next and previous program

Where is that information available in the stream? Is it PSI or an ID3 track?

Thanks for your answer! With Player.getDuration() i get this value -> -9223372036854775807 Where can I get metadata type? In vlc i can't see anything

AquilesCanta commented 3 years ago

That value is C.TIME_UNSET, which means that the duration of the transport stream is unknown. Hard to know the reason without a sample stream, could be that the server is not defining the resource's length, for instance.

Where can I get metadata type? In vlc i can't see anything

I don't understand this. Can you very clearly explain what you are trying to achieve? In other words, where is Exoplayer expected to obtain the following information from:

in playback title, next and previous program etc.

danisebastio commented 3 years ago

That value is C.TIME_UNSET, which means that the duration of the transport stream is unknown. Hard to know the reason without a sample stream, could be that the server is not defining the resource's length, for instance.

Where can I get metadata type? In vlc i can't see anything

I don't understand this. Can you very clearly explain what you are trying to achieve? In other words, where is Exoplayer expected to obtain the following information from:

in playback title, next and previous program etc.

I leave my stream code here:

class MoviePlayerActivity : AppCompatActivity(), View.OnClickListener, Player.EventListener, SeekBar.OnSeekBarChangeListener, MediaSourceFactory, MetadataOutput {

private var controllerVisible: Boolean = false
private var simpleExoPlayer: SimpleExoPlayer? = null
//Create default UDP Datasource and mediasource
private val dataSourceFactory =
    DataSource.Factory {
        UdpDataSource(
            300000,
            0
        )
    }

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_movie_player)
    hideSystemUI()
    setFullScreenAndHideActionBar()
    setUIControls()

    val trackSelector =
        DefaultTrackSelector(AdaptiveTrackSelection.Factory())

   //HERE I RETRIEVE URL STREAM FROM PREVIOUS ACTIVITY THAT IS "udp://@239.72.31.252" or "udp://@239.72.31.253"
    var videoUri: Uri? = null
    if (intent.getStringExtra("url") != "")
        videoUri = Uri.parse(intent.getStringExtra("url"))

    if (intent.getStringExtra("programName") != "")
        txt_player_title.text = intent.getStringExtra("programName")

    img_player_back?.setOnClickListener(this)
    exo_progress_live?.setOnSeekBarChangeListener(this)

    //Create the player and playerview
    simpleExoPlayer = SimpleExoPlayer.Builder(applicationContext).build()

    val mediaSource = buildMediaSource(videoUri)

    // set player in playerView
    val playerView = findViewById<PlayerView>(R.id.player_view)
    playerView.player = simpleExoPlayer
    playerView.requestFocus()

    simpleExoPlayer?.addListener(this)
    simpleExoPlayer?.prepare(mediaSource)
    simpleExoPlayer?.playWhenReady = true

    playerView.setControllerVisibilityListener {
    }

    simpleExoPlayer?.addListener(object : Player.EventListener {

        override fun onPlayerStateChanged(playWhenReady: Boolean, playbackState: Int) {
            if (playbackState == Player.STATE_BUFFERING)
                progressBar.visibility = VISIBLE
            else if (playbackState == Player.STATE_READY) {
                progressBar.visibility = GONE
            }
        }

        //PROGRAM ENTERS HERE IN DEBUG MODE AFTER I START STREAM FROM VLC
        override fun onTracksChanged(
            trackGroups: TrackGroupArray,
            trackSelections: TrackSelectionArray
        ) {
            for (i in 0 until trackGroups.length) {
                val trackGroup = trackGroups.get(i)
                for (j in 0 until trackGroup.length) {
                    val trackMetadata = trackGroup.getFormat(j)
                    if (trackMetadata != null) {
            //do something with metadata
                    }
                }
            }
        }
    })

}

override fun onTimelineChanged(timeline: Timeline, reason: Int) {}

override fun onDestroy() {
    super.onDestroy()
    simpleExoPlayer?.release()
}

private fun setFullScreenAndHideActionBar() {
    window.setFlags(
        WindowManager.LayoutParams.FLAG_FULLSCREEN,
        WindowManager.LayoutParams.FLAG_FULLSCREEN
    )
    supportActionBar?.hide()
}

override fun onClick(v: View?) {
    when (v?.id) {
        R.id.img_player_back -> {
            this.onBackPressed()
        }
    }
}

private fun hideSystemUI() {
    val handler = Handler()
    handler.postDelayed(object : Runnable {
        override fun run() {
            window.decorView.systemUiVisibility = (View.SYSTEM_UI_FLAG_LAYOUT_STABLE
                    or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
                    or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                    or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION // hide nav bar
                    or View.SYSTEM_UI_FLAG_FULLSCREEN // hide status bar
                    or View.SYSTEM_UI_FLAG_IMMERSIVE)
            handler.postDelayed(this, 5000)
        }
    }, 0)

}

private fun setUIControls() {
    exo_progress_live?.visibility = VISIBLE
    exo_duration_live?.visibility = VISIBLE
    exo_progress_live?.isEnabled = false
    exo_progress?.visibility = GONE
}

override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {}

override fun onStartTrackingTouch(seekBar: SeekBar?) {}

override fun onStopTrackingTouch(seekBar: SeekBar?) {}

private fun buildMediaSource(uri: Uri?): MediaSource {
    return when (@C.ContentType val type = Util.inferContentType(uri!!)) {
        C.TYPE_DASH ->
            DashMediaSource.Factory(dataSourceFactory).createMediaSource(uri)
        C.TYPE_SS ->
            SsMediaSource.Factory(dataSourceFactory).createMediaSource(uri)
        C.TYPE_HLS ->
            HlsMediaSource.Factory(dataSourceFactory).createMediaSource(uri)
        C.TYPE_OTHER -> {
            val extractorsFactory =
                DefaultExtractorsFactory()
                    .setTsExtractorFlags(FLAG_ALLOW_NON_IDR_KEYFRAMES)
            ProgressiveMediaSource.Factory(dataSourceFactory, extractorsFactory)
                .createMediaSource(uri)
        }
        else ->
            throw IllegalStateException("Unsupported type: $type")
    }
}

override fun getSupportedTypes(): IntArray {
    return intArrayOf(C.TYPE_DASH, C.TYPE_HLS, C.TYPE_OTHER)
}

override fun setDrmSessionManager(drmSessionManager: DrmSessionManager<*>?): MediaSourceFactory? {
    return null
}

override fun createMediaSource(uri: Uri?): MediaSource {
    return buildMediaSource(uri)
}

override fun onMetadata(metadata: Metadata) {
    Log.i("qui", "metadata $metadata")
}

override fun dispatchKeyEvent(event: KeyEvent): Boolean {
    if (event.keyCode == KeyEvent.KEYCODE_ENTER) {
        if (event.action == KeyEvent.ACTION_UP) {
            when (event.keyCode) {
                KeyEvent.KEYCODE_BUTTON_SELECT, KeyEvent.KEYCODE_DPAD_CENTER, KeyEvent.KEYCODE_ENTER, KeyEvent.KEYCODE_NUMPAD_ENTER -> {
                    if (!player_view.isControllerVisible)
                        player_view.showController()

                    simpleExoPlayer?.playWhenReady = !simpleExoPlayer?.playWhenReady!!

                }

            }
        }
    }
    return super.dispatchKeyEvent(event)
}

}

I think that in "onTracksChanged" method i can achieve metadata. The problem is that i receive only video resolution (1920x1080). In the previous your answer, you ask me which metadata type i expect but I don't know where i can retrieve this information, can u tell me how can i? Sorry but I'm new in streming.

Immagine

And i need also to retrieve the title at the top of vlc in my app and i can't retrieve this information in my metadata from code.

sandles commented 2 years ago

Hi,

I have a similar use case for what I think this original poster was asking. Is there anyway with ExoPlayer to extract the Metadata from a UDP TS stream.

Yes, this is referring to the data stored in the PSI part of the stream. In particular the Title Name / Now Playing info.

Thanks