具有身份验证的MediaPlayer RTSP视频流

时间:2013-11-14 14:08:14

标签: android video-streaming android-mediaplayer rtsp

我能够在未经授权的情况下从ip camera流式传输视频,但现在我需要通过授权进行此操作。我发现很少有信息表明Android不支持RTSP身份验证,但我发现另一个信息表明,通过使用该方法添加HEADERS,可以在API级别14中使用setDataSource (Context context, Uri uri, Map headers)。我的代码如下所示:

@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类新方法的效果?

2 个答案:

答案 0 :(得分:2)

这样给出,rtsp:// username:password@10.0.0.113:554 / channel1。如果服务器期望基于MD5摘要的身份验证,那么Android 4.2到4.2还不够好。

答案 1 :(得分:2)

RTSP使用TCP控制套接字执行握手并发送控制消息。实际视频数据通过TCP或UDP中的单独端口发送。 RTSP控制信息也可以通过HTTP或HTTPS进行隧道传输。我使用Android MediaPlayer遇到的问题是它不支持RTSP身份验证。但是,通过创建添加身份验证信息的本地RTSP代理,我能够绕过该限制。幸运的是,RTSP标头非常简单明了,因此添加必要的身份验证很容易。

我是这样做的:

  1. 创建一个本地ServerSocket作为代理。
  2. 从用户密码
  3. 构建身份验证字符串
  4. 将MediaPlayer连接到本地代理
  5. 对于传出数据,将身份验证添加到传出TCP数据包
  6. 请注意,RTSP数据非常稀疏,因此解析和更改所有RTSP数据的开销通常可以忽略不计。

    以下是一些显示我的意思的示例代码:

    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();
            }
        }
    }