MediaPlayer RTSP video stream with authentication

时光怂恿深爱的人放手 提交于 2019-12-05 03:16:22

问题


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:

  1. Create a local ServerSocket to act as a proxy.
  2. Build the authentication string from the user's password
  3. Connect the MediaPlayer to your local proxy
  4. 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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!