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:
When I attempt to fetch the RTSP resource I fail here:
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!
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:
package net.cudasystems.android.videotest;
import android.net.Uri;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import com.google.android.exoplayer2.DefaultLoadControl;
import com.google.android.exoplayer2.DefaultRenderersFactory;
import com.google.android.exoplayer2.ExoPlayer;
import com.google.android.exoplayer2.ExoPlayerFactory;
import com.google.android.exoplayer2.source.ExtractorMediaSource;
import com.google.android.exoplayer2.source.MediaSource;
import com.google.android.exoplayer2.source.rtsp.RtspMediaSource;
import com.google.android.exoplayer2.trackselection.DefaultTrackSelector;
import com.google.android.exoplayer2.ui.PlayerView;
import com.google.android.exoplayer2.upstream.DataSource;
import com.google.android.exoplayer2.upstream.DefaultHttpDataSourceFactory;
import com.google.android.exoplayer2.util.Util;
public class MainActivity extends AppCompatActivity {
private String mURL = "rtsp://192.168.4.211:554/cam/realmonitor?channel=1&subtype=0";
// private String mURL = "http://www.myserver.net/admit.mp4";
PlayerView mPlayerView;
ExoPlayer player;
private DataSource.Factory mediaDataSourceFactory;
private long playbackPosition;
private int currentWindow;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mPlayerView = findViewById(R.id.video_view);
}
private void initializePlayer() {
player = ExoPlayerFactory.newSimpleInstance(
new DefaultRenderersFactory(this),
new DefaultTrackSelector(), new DefaultLoadControl());
mPlayerView.setPlayer(player);
player.setPlayWhenReady(true);
player.seekTo(currentWindow, playbackPosition);
Uri uri = Uri.parse(mURL);
MediaSource mediaSource = buildMediaSource(uri);
player.prepare(mediaSource, true, false);
}
private MediaSource buildMediaSource(Uri uri) {
return new ExtractorMediaSource.Factory(
new DefaultHttpDataSourceFactory("exoplayer-codelab")).
createMediaSource(uri);
//return new RtspMediaSource.Factory(mediaDataSourceFactory)
// .createMediaSource(uri);
}
@Override
public void onStart() {
super.onStart();
if (Util.SDK_INT > 23) {
initializePlayer();
}
}
@Override
public void onResume() {
super.onResume();
if ((Util.SDK_INT <= 23 || player != null)) {
initializePlayer();
}
}
@Override
public void onPause() {
super.onPause();
if (Util.SDK_INT <= 23) {
releasePlayer();
}
}
@Override
public void onStop() {
super.onStop();
if (Util.SDK_INT > 23) {
releasePlayer();
}
}
private void releasePlayer() {
if (player != null) {
playbackPosition = player.getCurrentPosition();
currentWindow = player.getCurrentWindowIndex();
player.release();
player = null;
}
}
}
When I attempt to fetch the RTSP resource I fail here:
Code:
05-18 15:57:16.918 13071-13088/net.cudasystems.android.videotest E/ExoPlayerImplInternal: Source error.
com.google.android.exoplayer2.upstream.HttpDataSource$HttpDataSourceException: Unable to connect to rtsp://192.168.4.211:554/cam/realmonitor?channel=1&subtype=0
at com.google.android.exoplayer2.upstream.DefaultHttpDataSource.open(DefaultHttpDataSource.java:194)
at com.google.android.exoplayer2.source.ExtractorMediaPeriod$ExtractingLoadable.load(ExtractorMediaPeriod.java:858)
at com.google.android.exoplayer2.upstream.Loader$LoadTask.run(Loader.java:317)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1113)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588)
at java.lang.Thread.run(Thread.java:818)
Caused by: java.net.MalformedURLException: Unknown protocol: rtsp
at java.net.URL.<init>(URL.java:182)
at java.net.URL.<init>(URL.java:125)
at com.google.android.exoplayer2.upstream.DefaultHttpDataSource.makeConnection(DefaultHttpDataSource.java:341)
at com.google.android.exoplayer2.upstream.DefaultHttpDataSource.open(DefaultHttpDataSource.java:192)
at com.google.android.exoplayer2.source.ExtractorMediaPeriod$ExtractingLoadable.load(ExtractorMediaPeriod.java:858)
at com.google.android.exoplayer2.upstream.Loader$LoadTask.run(Loader.java:317)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1113)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588)
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!