问题
I was able to stream a video from ip camera without authorization, but now i need to do this with authorization. I found few information that says that Android doesn't support authentication by RTSP, but I found another information that says that it is possible in API level 14 by adding HEADERS with that method: setDataSource (Context context, Uri uri, Map headers). My code looks like this:
@Override
public void surfaceCreated(SurfaceHolder holder){
String authHeader = getB64Auth("user","password");
Map<String, String> headers = new HashMap<String, String>();
headers.put("Authorization", authHeader);
Uri srcUri = Uri.parse("rtsp://10.0.0.113:554/channel1");
try{
m.setDisplay(h);
m.setDataSource (getApplicationContext(), srcUri,headers);
m.prepare();
m.setAudioStreamType(AudioManager.STREAM_MUSIC);
m.start();
}catch(Exception e){
e.printStackTrace();
}
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
m.release();
}
private String getB64Auth (String login, String pass) {
String source=login+":"+pass;
String ret="Basic "+Base64.encodeToString(source.getBytes(),Base64.URL_SAFE|Base64.NO_WRAP);
Log.e("Authorization",ret);
return ret;
}
But it doesn't work and i can't find where is the problem. Does anybody have any experience with that kind of streaming? Or maybe I just misunderstood the effect of the new method of MediaPlayer class?
回答1:
Give like this, rtsp://username:password@10.0.0.113:554/channel1. And if the server expects MD5 digest based authentication, then Android upto 4.2 was not good enough for that.
回答2:
RTSP uses a TCP control socket to perform handshaking and send control messages. The actual video data is sent across a separate port in TCP or UDP. RTSP control information can also be tunneled through HTTP or HTTPS. The problem that I ran into with using Android MediaPlayer was that it does not support RTSP authentication. However, I was able to get around that limitation by creating a local RTSP proxy that adds the authentication information. Luckily, the RTSP header is very simple and clear text, so adding the necessary authentication is easy.
Here is how I did it:
- Create a local ServerSocket to act as a proxy.
- Build the authentication string from the user's password
- Connect the MediaPlayer to your local proxy
- For outgoing data, add the authentication to the outgoing TCP packet
Note that the RTSP data is pretty sparse so the overhead of parsing and changing all RTSP data is usually negligible.
Here is some example code to show what I mean:
import android.util.Base64;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.Map;
public class RTSPConnection implements Runnable {
private final IRTSPListener listener;
private String targetRtspServerHost;
private String targetRtspServerProto;
private String targetRtspUserInfo;
private String targetRtspServerPath;
private int targetRtspServerPort = -1;
public RTSPConnection(IRTSPListener l, String u) throws RTSPException {
// Parse the host (content provider) from the URL
this.listener = l;
if (u == null) {
throw new RTSPException();
}
URI parsedURL;
try {
parsedURL = new URI(u);
} catch (URISyntaxException e) {
throw new RTSPException();
}
targetRtspServerHost = parsedURL.getHost(); // none is null
if (targetRtspServerHost == null || targetRtspServerHost.equals("")) {
throw new RTSPException();
}
targetRtspServerProto = parsedURL.getScheme(); // none is null
if (targetRtspServerProto == null) {
targetRtspServerProto = "rtsp"; // default to rtsp:
}
if (!targetRtspServerProto.equalsIgnoreCase("rtsp")) {
throw new RTSPException(); // error for http: or https:
}
targetRtspServerPort = parsedURL.getPort(); // none is -1
if (targetRtspServerPort == -1) {
targetRtspServerPort = 554; // Use the RTSP default port
}
targetRtspUserInfo = parsedURL.getUserInfo(); // none is null
targetRtspServerPath = parsedURL.getPath(); // none is an empty string ""
}
@Override
public void run() {
ServerSocket listen;
try {
// Start the local proxy server on a random high port
listen = new ServerSocket(0);
} catch (IOException e) {
// Error starting local server
}
// Connects to the RTSP server
Socket remote;
try {
remote = new Socket(targetRtspServerHost, targetRtspServerPort);
} catch (IOException e) {
try {
listen.close();
} catch (IOException ignored) {}
return;
}
// Notify user on other thread that the server is about to start
// Build the string that the VideoPlayer should connect to in order to be routed to the actual source
int listeningOnPort = listen.getLocalPort();
String connectUrl = targetRtspServerProto + "://localhost:" + listeningOnPort + targetRtspServerPath;
listener.onRtspProxyReady(connectUrl);
// Wait for a local connection (Blocking)
Socket local;
try {
// Wait for 5 seconds for a connection
listen.setSoTimeout(5000);
local = listen.accept();
} catch (IOException e) {
// Error on server connect/accept or timed out waiting
try {
listen.close();
} catch (IOException ignored) {}
try {
remote.close();
} catch (IOException ignored) {}
listener.onRtspProxyError(RTSPError.ProxyTimedOut);
return;
}
// Create the Authorization header for the outgoing RTSP packets
Map<String, String> headers = null;
if (targetRtspUserInfo != null) {
String authHeader = "Basic " + Base64.encodeToString(targetRtspUserInfo.getBytes(),
Base64.URL_SAFE | Base64.NO_WRAP);
headers = new HashMap<String, String>();
headers.put("Authorization", authHeader);
}
try {
new ForwardAndAddHeadersThread(local, remote, headers).start();
new PassThru(remote, local).start();
} catch (IOException e) {
try {
local.close();
} catch (IOException ignored) {}
try {
listen.close();
} catch (IOException ignored) {}
try {
remote.close();
} catch (IOException ignored) {}
listener.onRtspProxyError(RTSPError.CouldNotStartProxy);
}
}
private abstract class ForwardTCPThread extends Thread {
protected InputStream in;
protected OutputStream out;
private Socket socket_in, socket_out;
public ForwardTCPThread(Socket i, Socket o) throws IOException {
socket_in = i;
socket_out = o;
in = socket_in.getInputStream();
out = socket_out.getOutputStream();
}
protected void shutdown() {
// Close things down...
if (in != null) {
try {
in.close();
} catch (IOException ignored) {}
}
if (out != null) {
try {
out.close();
} catch (IOException ignored) {}
}
try {
socket_in.close();
} catch (IOException ignored) {}
try {
socket_out.close();
} catch (IOException ignored) {}
}
}
private class PassThru extends ForwardTCPThread {
public PassThru(Socket in, Socket out) throws IOException {
super(in, out);
setName("Forward TCP");
}
public void run() {
byte[] buf = new byte[4096];
try {
int count;
while ((count = in.read(buf)) > 0) {
out.write(buf, 0, count);
}
} catch (IOException e) {
listener.onRtspProxyError(RTSPError.RemoteConnectionDropped);
}
shutdown();
}
}
public class ForwardAndAddHeadersThread extends ForwardTCPThread {
private final Map<String,String> headers;
public ForwardAndAddHeadersThread(Socket in, Socket out, Map<String,String> headers) throws IOException {
super(in, out);
this.headers = headers;
setName("Forward TCP Add Headers");
}
public void run() {
byte[] buf = new byte[4096];
try {
int count;
/*
* This code looks for the sequence number header in the RTSP packet and inserts additional headers
* on the next rows, separated by \r\n
*/
while ((count = in.read(buf)) > 0) {
/**
* Note: This code is NOT optimized for speed. It is assumed to be a very low data channel that
* only contains infrequent/short TCP/RTSP commands.
*
* Warn: This code assumes that the RTSP packet is read all-at-once, such that the CSeq: header
* is never split into two "reads".
*/
String temp = new String(buf, 0, count, "UTF-8");
String strings[] = temp.split("\r\n",-1);
String str_buf = "";
for (String s: strings) {
str_buf += s + "\r\n";
if (headers != null) {
if (s.contains("CSeq:")) {
for (Map.Entry<String, String> entry : headers.entrySet()) {
str_buf += entry.getKey() + ": " + entry.getValue() + "\r\n";
}
}
}
}
out.write(str_buf.getBytes("UTF-8"), 0, str_buf.length());
}
} catch (IOException e) {
listener.onRtspProxyError(RTSPError.LocalConnectionDropped);
}
shutdown();
}
}
}
来源:https://stackoverflow.com/questions/19979818/mediaplayer-rtsp-video-stream-with-authentication