ExoPlayer icon indicating copy to clipboard operation
ExoPlayer copied to clipboard

Secure Reliable Transport (SRT) support

Open kaweesi opened this issue 3 years ago • 23 comments

Use case description

As a programmer/user, i want to be able to stream an SRT url

Proposed solution

Add SRT support to exoplayer

Alternatives considered

Using a separate SRT player

kaweesi avatar Feb 28 '21 20:02 kaweesi

@ojw28 are you working on this?

kaweesi avatar Mar 09 '21 07:03 kaweesi

As of now, we do not have a concrete plan for working on this.

ojw28 avatar Mar 09 '21 14:03 ojw28

If i wanted to work on this, could i be paired with someone to ease my work with pointers!

kaweesi avatar Mar 09 '21 14:03 kaweesi

The core engineering team does not have resources to invest in this area in the short term. This includes the kind of arrangement you describe, since in practice such arrangements require significant time investment, and any time invested is time not invested in some other, higher priority work items.

If there's significant interest in this feature, you may find someone else external to the core engineering team who's interested in helping. This issue serves as a good place for interested developers to find one another.

ojw28 avatar Mar 09 '21 14:03 ojw28

where can i find any such external devs to help with such an interest as this even more?

kaweesi avatar Jul 12 '21 14:07 kaweesi

I managed to implement a custom DataSource that works with live streaming SRT. I'll leave it here for anyone who might need it. You can also modify it a bit to work with non-live streams.

Uses srtdroid library

Factory

class SrtLiveStreamDataSourceFactory(
    private val srtUrl: String,
    private val port: Int,
    private val passPhrase: String? = null
) :
    DataSource.Factory {
    override fun createDataSource(): DataSource {
        return SrtLiveStreamDataSource(srtUrl, port, passPhrase)
    }
}

DataSource

const val PAYLOAD_SIZE = 1316


class SrtLiveStreamDataSource(
    private val srtUrl: String,
    private val port: Int,
    private val passPhrase: String?

) :
    BaseDataSource(/*isNetwork*/true) {

    private var socket: Socket? = null
    private val byteQueue: Queue<Byte> = LinkedList()


    override fun open(dataSpec: DataSpec): Long {
        socket = Socket()
        socket?.setSockFlag(SockOpt.TRANSTYPE, Transtype.LIVE)
        socket?.setSockFlag(SockOpt.PAYLOADSIZE, PAYLOAD_SIZE)
        if(passPhrase != null){
            socket?.setSockFlag(SockOpt.PASSPHRASE, passPhrase)
        }
        socket?.connect(srtUrl, port)
        return C.LENGTH_UNSET.toLong()
    }


    /**
     * Receives from SRT socket and feeds into a queue. Depending on the length requested
     * from exoplayer, that amount of bytes is polled from queue and onto the buffer with the given offset.
     *
     * You cannot directly receive at the given length from the socket, because SRT uses a
     * predetermined payload size that cannot be dynamic
     */
    override fun read(buffer: ByteArray, offset: Int, length: Int): Int {
        if (length == 0) {
            return 0
        }
        var bytesReceived = 0
        if (socket != null) {
            val received = socket!!.recv(PAYLOAD_SIZE)
            for (byte in received.second /*received byte array*/) {
                byteQueue.offer(byte)
            }
            repeat(length) { index ->
                val byte = byteQueue.poll()
                if (byte != null) {
                    buffer[index + offset] = byte
                    bytesReceived++
                }
            }
            return bytesReceived
        }
        throw IOException("Couldn't read bytes at offset: $offset")
    }

    override fun getUri(): Uri? {
        return Uri.parse("srt://$srtUrl:$port")
    }

    override fun close() {
        socket?.close()
        socket = null
    }
}

Simple usage

class MainActivity : AppCompatActivity() {

    private lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)


        val url = SRT_URL_HERE (exclude srt://)
        val port = PORT_HERE

        val source = ProgressiveMediaSource.Factory(
            SrtLiveStreamDataSourceFactory(
                url,
                port,
            ),
        ).createMediaSource(MediaItem.fromUri(Uri.EMPTY))


        val player = ExoPlayer.Builder(this)
            .build()
        player.setMediaSource(source)
        binding.playerView.player = player


        player.prepare()
        player.play()
        player.playWhenReady = true

    }
}

YoussefHenna avatar Nov 12 '21 07:11 YoussefHenna

@YoussefHenna I am trying to test your code, the player connects but it won't display the stream. Where did you get your SRT live stream from?

ThibaultBee avatar Nov 13 '21 13:11 ThibaultBee

I run srt-live-transmit from a local machine and stream to it using OBS. And following the code above, it autoplays after a couple of seconds of preparing and showing a black screen. All devices involved are on the same local network.

I tested the app available here on my local environment and it's working as expected. It's probably an issue from your SRT setup rather than with the app. Let me know if I can help further.

@ThibaultBee

YoussefHenna avatar Nov 13 '21 13:11 YoussefHenna

Thank you for your reply but I still can get read the stream. I guess something is wrong with how I use the player. I have the feeling that Exoplayer does not understand it receive a TS file. On ffmpeg side, I execute: ffmpeg -f lavfi -re -i smptebars=duration=300:size=1280x720:rate=30 -f lavfi -re -i sine=frequency=1000:duration=60:sample_rate=44100 -pix_fmt yuv420p -c:v libx264 -b:v 1000k -g 30 -keyint_min 120 -profile:v baseline -preset veryfast -f mpegts "udp://127.0.0.1:1234?pkt_size=1316"

On srt live transmit: srt-live-transmit udp://:1234 srt://:9998 -v

These comes from SRT documentation. What is your configurations?

Also, Exoplayer datasource read a first buffer of size 3. Can we avoid this? To avoid this issue, you made a FIFO but it is not very efficient.

ThibaultBee avatar Nov 15 '21 18:11 ThibaultBee

The only difference between your setup and mine is that I use srt:// in both source and destination URLs. Whenever I used udp:// in the source URL, I couldn't get the stream working on any SRT player (VLC or even the Haivision mobile app).

Try something like this srt-live-transmit srt://:1234 srt://:9998 -v and try it out. I'm not sure if this is an issue with srt-live-transmit or maybe I'm missing something, but this is how I got it working.

As for the initial small buffer size, this was the main issue I faced when trying to get it working. The queue is the only solution I came up with that worked. Since SRT has to send the bytes at a fixed size (defaults to 1316), there was no way to just request 3 bytes at the start and increase incrementally. And from my research, exoplayer does not provide a way to overcome this.

YoussefHenna avatar Nov 16 '21 07:11 YoussefHenna

Hi, I am not how I made it work, could be your srt://``tip or the fact that I change few things in the ffmpeg` to reduce gop size. Anyway, I also improve the way Exoplayer read SRT data: data are read by buffer of TS_BUFFER_SIZE(188 bytes). It is not perfect but it is more efficient. You can have a look on my project.

Thank you for great example :)

ThibaultBee avatar Nov 16 '21 20:11 ThibaultBee

Glad you got it working! Also like the optimization, I'll definitely add it to my project too.

I'm not sure if this is the plan or not, but you should definitely make your project public once its all done. It would be a great addition to the community.

You've done great work towards SRT support on Android already, so thanks a lot!

YoussefHenna avatar Nov 16 '21 21:11 YoussefHenna

I'm not sure if this is the plan or not, but you should definitely make your project public once its all done. It would be a great addition to the community.

I am going to make it public but I don't like the fact that it is just a demo. It is not like it is ExoPlayer SRT support but I don't have time for a proper implementation!

You've done great work towards SRT support on Android already, so thanks a lot!

You did as well!

ThibaultBee avatar Nov 16 '21 21:11 ThibaultBee

@ThibaultBee did u make it public

Muta-Jonathan avatar Feb 22 '22 23:02 Muta-Jonathan

@Muta-Jonathan. Yes, have a look at https://github.com/ThibaultBee/SrtPlayer. But it is just a POC so I don't recommend to use it in production. It does not replace a proper implementation in ExoPlayer.

ThibaultBee avatar Feb 24 '22 12:02 ThibaultBee

Thanks, @ThibaultBee though while testing the app a null pointer is thrown ....at the opening of the app

Muta-Jonathan avatar Feb 25 '22 07:02 Muta-Jonathan

Hum, this is not the place for reporting bug for other project. Can you add an issue on SrtPlayer with a logcat please?

ThibaultBee avatar Feb 25 '22 07:02 ThibaultBee

Hi @YoussefHenna I tested https://github.com/ThibaultBee/SrtPlayer , but video player keep showing in black. Any idea?

linhnv106 avatar Nov 07 '23 06:11 linhnv106

Hi @YoussefHenna I tested https://github.com/ThibaultBee/SrtPlayer , but video player keep showing in black. Any idea?

@linhnv106 I'm not sure, need some more information. Could you let me know if you are getting any warnings or errors? Are you able to play the video on VLC? Do you have a minimal reproducible example?

YoussefHenna avatar Nov 07 '23 16:11 YoussefHenna

Hi @YoussefHenna I tested https://github.com/ThibaultBee/SrtPlayer , but video player keep showing in black. Any idea?

@linhnv106 I'm not sure, need some more information. Could you let me know if you are getting any warnings or errors? Are you able to play the video on VLC? Do you have a minimal reproducible example?

Hi @YoussefHenna , Yes I can play the url with VLC App. Was you able to play the srt:// url with exoplayer?. please find the log from here : https://github.com/ThibaultBee/SrtPlayer/issues/1

linhnv106 avatar Nov 07 '23 17:11 linhnv106

@linhnv106 Have you tried implementing your own data source as I wrote here: https://github.com/google/ExoPlayer/issues/8647#issuecomment-966878405. I'm not really sure of the changes made on the SrtPlayer repo

YoussefHenna avatar Nov 07 '23 17:11 YoussefHenna

@tonihei any progress on this to be intergrated in exoplayer?

Muta-Jonathan avatar Nov 08 '23 06:11 Muta-Jonathan

@linhnv106 Have you tried implementing your own data source as I wrote here: #8647 (comment). I'm not really sure of the changes made on the SrtPlayer repo

Yes @YoussefHenna I'm using your snip code

linhnv106 avatar Nov 08 '23 08:11 linhnv106