具有身份验证的MediaPlayer RTSP视频流 [英] MediaPlayer RTSP video stream with authentication
问题描述
我能够在未经授权的情况下从ip摄像机流式传输视频,但是现在我需要在授权下进行此操作.我发现很少有信息表明Android不支持通过RTSP进行身份验证,但是我发现另一个信息表明,通过使用该方法添加HEADERS,可以在API级别14中实现: .com/reference/android/media/MediaPlayer.html#setDataSource%28android.content.Context,%20android.net.Uri,%20java.util.Map%3Cjava.lang.String,%20java.lang.String%3E% 29"rel =" nofollow> setDataSource(上下文上下文,Uri uri,地图标题).我的代码如下:
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;
}
但是它不起作用,我找不到问题出在哪里.有人对这种流媒体有任何经验吗?还是我只是误解了MediaPlayer类的新方法的效果?
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?
推荐答案
RTSP使用TCP控制套接字执行握手并发送控制消息.实际的视频数据通过TCP或UDP中的单独端口发送. RTSP控制信息也可以通过HTTP或HTTPS进行隧道传输.我在使用Android MediaPlayer时遇到的问题是它不支持RTSP身份验证.但是,我能够通过创建添加身份验证信息的本地RTSP代理来解决该限制.幸运的是,RTSP标头非常简单明了,因此添加必要的身份验证很容易.
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.
这是我的做法:
- 创建一个本地ServerSocket充当代理.
- 根据用户密码构建身份验证字符串
- 将MediaPlayer连接到本地代理
- 对于传出数据,将身份验证添加到传出TCP数据包中
请注意,RTSP数据非常稀疏,因此解析和更改所有RTSP数据的开销通常可以忽略不计.
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();
}
}
}
这篇关于具有身份验证的MediaPlayer RTSP视频流的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!