1. Are you ready for the Galaxy S20? Here is everything we know so far!

ExoPlayer - Attaching Data Sources

Discussion in 'Android Development' started by tickerguy, May 18, 2018.

  1. tickerguy

    tickerguy Android Enthusiast
    Thread Starter

    There's an add-on recently put out (and under submission for a pull request) that will add rtsp support to the ExoPlayer. This is extremely useful, since the base code (which is part of android and not an externally-visible thing) is broken with digest authentication (it adds a space to the URI and thus the verification fails!) and who knows when or if Google will fix it.

    However, I'm having a devil of a time figuring out a proper constructor for this thing -- and it's more-or-less generalized as well.

    Here's a lightly-modified version from the Google CodeFactory example that works for a MP3 or other "http" fetched resourced but fails with an "Rtsp" one:

    Code (Text):
    2. package net.cudasystems.android.videotest;
    5. import android.net.Uri;
    6. import android.os.Bundle;
    7. import android.support.v7.app.AppCompatActivity;
    9. import com.google.android.exoplayer2.DefaultLoadControl;
    10. import com.google.android.exoplayer2.DefaultRenderersFactory;
    11. import com.google.android.exoplayer2.ExoPlayer;
    12. import com.google.android.exoplayer2.ExoPlayerFactory;
    13. import com.google.android.exoplayer2.source.ExtractorMediaSource;
    14. import com.google.android.exoplayer2.source.MediaSource;
    15. import com.google.android.exoplayer2.source.rtsp.RtspMediaSource;
    16. import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
    17. import com.google.android.exoplayer2.ui.PlayerView;
    18. import com.google.android.exoplayer2.upstream.DataSource;
    19. import com.google.android.exoplayer2.upstream.DefaultHttpDataSourceFactory;
    20. import com.google.android.exoplayer2.util.Util;
    23. public class MainActivity extends AppCompatActivity {
    25.     private String mURL = "rtsp://";
    26.     // private String mURL = "http://www.myserver.net/admit.mp4";
    28.     PlayerView mPlayerView;
    30.     ExoPlayer player;
    32.     private DataSource.Factory mediaDataSourceFactory;
    34.     private long playbackPosition;
    35.     private int currentWindow;
    37.     @Override
    38.     protected void onCreate(Bundle savedInstanceState) {
    39.         super.onCreate(savedInstanceState);
    40.         setContentView(R.layout.activity_main);
    42.         mPlayerView = findViewById(R.id.video_view);
    44.     }
    46.     private void initializePlayer() {
    47.         player = ExoPlayerFactory.newSimpleInstance(
    48.                 new DefaultRenderersFactory(this),
    49.                 new DefaultTrackSelector(), new DefaultLoadControl());
    51.         mPlayerView.setPlayer(player);
    53.         player.setPlayWhenReady(true);
    54.         player.seekTo(currentWindow, playbackPosition);
    56.         Uri uri = Uri.parse(mURL);
    57.         MediaSource mediaSource = buildMediaSource(uri);
    58.         player.prepare(mediaSource, true, false);
    59.     }
    61.     private MediaSource buildMediaSource(Uri uri) {
    62.         return new ExtractorMediaSource.Factory(
    63.                 new DefaultHttpDataSourceFactory("exoplayer-codelab")).
    64.                 createMediaSource(uri);
    66.         //return new RtspMediaSource.Factory(mediaDataSourceFactory)
    67.         //        .createMediaSource(uri);
    69.     }
    71.     @Override
    72.     public void onStart() {
    73.         super.onStart();
    74.         if (Util.SDK_INT > 23) {
    75.             initializePlayer();
    76.         }
    77.     }
    79.     @Override
    80.     public void onResume() {
    81.         super.onResume();
    82.         if ((Util.SDK_INT <= 23 || player != null)) {
    83.             initializePlayer();
    84.         }
    85.     }
    86.     @Override
    87.     public void onPause() {
    88.         super.onPause();
    89.         if (Util.SDK_INT <= 23) {
    90.             releasePlayer();
    91.         }
    92.     }
    93.     @Override
    94.     public void onStop() {
    95.         super.onStop();
    96.         if (Util.SDK_INT > 23) {
    97.             releasePlayer();
    98.         }
    99.     }
    100.     private void releasePlayer() {
    101.         if (player != null) {
    102.             playbackPosition = player.getCurrentPosition();
    103.             currentWindow = player.getCurrentWindowIndex();
    104.             player.release();
    105.             player = null;
    106.         }
    107.     }
    109. }
    When I attempt to fetch the RTSP resource I fail here:
    Code (Text):
    2. 05-18 15:57:16.918 13071-13088/net.cudasystems.android.videotest E/ExoPlayerImplInternal: Source error.
    3.     com.google.android.exoplayer2.upstream.HttpDataSource$HttpDataSourceException: Unable to connect to rtsp://
    4.         at com.google.android.exoplayer2.upstream.DefaultHttpDataSource.open(DefaultHttpDataSource.java:194)
    5.         at com.google.android.exoplayer2.source.ExtractorMediaPeriod$ExtractingLoadable.load(ExtractorMediaPeriod.java:858)
    6.         at com.google.android.exoplayer2.upstream.Loader$LoadTask.run(Loader.java:317)
    7.         at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1113)
    8.         at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588)
    9.         at java.lang.Thread.run(Thread.java:818)
    10.      Caused by: java.net.MalformedURLException: Unknown protocol: rtsp
    11.         at java.net.URL.<init>(URL.java:182)
    12.         at java.net.URL.<init>(URL.java:125)
    13.         at com.google.android.exoplayer2.upstream.DefaultHttpDataSource.makeConnection(DefaultHttpDataSource.java:341)
    14.         at com.google.android.exoplayer2.upstream.DefaultHttpDataSource.open(DefaultHttpDataSource.java:192)
    15.         at com.google.android.exoplayer2.source.ExtractorMediaPeriod$ExtractingLoadable.load(ExtractorMediaPeriod.java:858)
    16.         at com.google.android.exoplayer2.upstream.Loader$LoadTask.run(Loader.java:317)
    17.         at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1113)
    18.         at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588)
    19.         at java.lang.Thread.run(Thread.java:818)
    So obviously the base HTTPdata source code, which calls java.net.URL, vehemently dislikes the protocol specification at the front. Fair enough, so I need a constructor for the data source that can open the connection and connect the socket.

    There is an RtspMediaSource.Factory class but it's not at all clear to me how to build that, and I can't find a decent example using one of the other similar media sources (eg. HLS, etc) that works. It says it wants a Builder but exactly what is necessary is not at all clear to me.

    The second question, assuming I can get this to build and run, is whether it's possible to "splice in" a different underlying transport into ExoPlayer's data loading scheme. The docs hint that it is, but I've yet to figure out the magic incantation for that either.

    What I'm trying to do is this:

    Assume a data resource that ExoPlayer knows how to talk to (in this case, an RTSP server.) Assume further it's behind a firewall, but on said firewall is a socket listener (e.g. SSL encrypted) that can take a connection, speak to the client (e.g. verify credentials, etc) and then if it's happy construct a tunnel to the target such that from the connecting external device's view it has a socket connection to the original source. Doing this on the server/firewall presents no challenges. Getting ExoPlayer to use it is, so far, a different matter.

    Thus what I want to do is build a method that can be attached and, at the point I attach it, the underlying ExoPlayer code (e.g. the RTSP protocol) simply sees it as a socket that looks like a connected instance directly to the remote (it's not, but the code there has no reason to be the wiser.)

    The base Android MediaPlayer has no exposed means to do this, but the ExoPlayer docs strongly imply you CAN do it -- but I've yet to figure out how.

    Any help with Part 1 or 2 appreciated!

    1. Download the Forums for Android™ app!


  2. Deleted User

    Deleted User Guest

  3. tickerguy

    tickerguy Android Enthusiast
    Thread Starter

    Yes, I've cloned it here and have played with it -- it only handles UDP transport, however, which is worthless if the device in question is behind some sort of DMZ device, and in addition it doesn't do anything for the underlying problem with RTSP over the Internet at-large (which is that the video data itself is entirely in the clear!)

    I'm trying to fix both problems (MD5 + unencrypted video data) at once for those devices where I can't do anything about it on the server (e.g. commercial webcams) end.
  4. Deleted User

    Deleted User Guest

    Have you had a dig around in the Exoplayer library to see what other types of data source classes are available? You're using DefaultHttpDataSource, which clearly knows nothing about rtsp. Is there an RtspDataSource class, which is what I'd maybe expect to find.
  5. tickerguy

    tickerguy Android Enthusiast
    Thread Starter

    Yeah I found the basic stuff to get it to play nice with some minimalist example code (and found and fixed a bug in the development version library that was sending a bogus OPTION stanza), but transport being UDP only kills its usefulness.

    There are basically no other-than-LAN applications in the "modern" world in which UDP is going to pass unmolested as a data "stream" beyond a boundary router, especially if the terminal (viewing) end is on some sort of public place like a WiFi network in a coffee shop. So to make this useful on the wider 'net, never mind the security aspects, I need to have an idea of where to start looking at plugging a transport into what's already there at the "appropriate points" -- which is where there are only a few hints of it being doable, and no good examples floating around I can find.

    It may be a bridge too far as it appears there's no support for interleaved data streams either (which are inherently necessary to use TCP as a transport since audio and video go over one socket), but that I might be able to handle demuxing without too much trouble.

    I'll tear into the libraries -- was hoping someone had perhaps done something similar already for some other purpose within ExoPlayer and had ideas on where to start looking.

Share This Page