在Spydroid-ipcamera基础上做推流的实现之二

接上一篇,已经清楚RTSP推流需要发送的RTSP请求消息,所以我们实现一个RTSP的客户端,完成这些请求消息,并将H264的RTP包推送到指定的RTSP服务器即可,这个客户端同时推送过Darwin和Crtmp-server两个RTSP的服务端,来验证推流客户端的功能实现。

下面是我们实现的RtspPushStreamClient推流客户端类:

package net.majorkernelpanic.streaming.rtmp;

import static net.majorkernelpanic.streaming.SessionBuilder.AUDIO_NONE;
import static net.majorkernelpanic.streaming.SessionBuilder.VIDEO_NONE;
import android.hardware.Camera.CameraInfo;
import android.os.Handler;
import android.util.Log; 

import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Random;

import net.majorkernelpanic.streaming.Session;
import net.majorkernelpanic.streaming.SessionBuilder;
import net.majorkernelpanic.streaming.rtsp.RtspClient;
import net.majorkernelpanic.streaming.rtsp.RtspClient.Callback;

/**
 * Created by aaa on 2015/11/25.
 */
public class RtspPushStreamClient implements Callback{
    private static final String TAG = "RtspPushStreamClient";
//    private static int localPort = 8100;
    RtspPushClient client;
    String mSdp;
    Handler mUserHandler;//

    public RtspPushStreamClient(Handler handler){
        client = new RtspPushClient(this); 
        mUserHandler= handler;
    }

    public void startRtspClient(String ip, String rtmp_path){  
      client.start_connect(ip, 554, rtmp_path); 
    }

    public void switchCamera(){
    	client.switchCamera();
    }

    public void setFlash(boolean mode){
    	client.setFlash(mode);
    }
    
    public void stopRtspClient(){
        client.stop_connect();
    }

    public static class RtspPushClient {
        private RtspClient client;
        private String request_uri;
        String mSdp;

        public  RtspPushClient(RtspPushStreamClient streamClient){
            client = new RtspClient(); 
            client.setCallback(streamClient);
        }

        public void start_connect(String ip, int port, String path){
            //"rtsp://192.168.0.5:9010/"
            client.setServerAddress(ip, port);		
            client.setStreamPath("/"+path);//"/live/rtsp_test"
        	
            //setCamera --CAMERA_FACING_FRONT
    		SessionBuilder builder = SessionBuilder.getInstance().clone();
//			builder.setCamera(CameraInfo.CAMERA_FACING_FRONT);
//			builder.setAudioEncoder(SessionBuilder.AUDIO_AAC).setVideoEncoder(SessionBuilder.VIDEO_H264);
			builder.setAudioEncoder(SessionBuilder.AUDIO_AAC).setVideoEncoder(SessionBuilder.VIDEO_H264);
            SessionBuilder b = SessionBuilder.getInstance();  
  
            Random rand = new Random();
            int localPort = rand.nextInt(1000)+ 8100; //8100-9100
            
            localPort = (localPort & 0xFFFE); /* turn to even number */
			Session session = builder.build(localPort); 
            client.setSession(session);
            client.startStream(localPort);  
        }

        public void switchCamera(){ 
        	client.switchCamera();
        }

        public void setFlash(boolean mode){
        	client.setFlash(mode);
        }
        
        public void stop_connect(){ 
        	client.stopStream();
        } 
    }

	@Override
	public void onRtspUpdate(int message, Exception exception) {
		// TODO Auto-generated method stub
		Log.d(TAG, "message:"+message);
		 
		mUserHandler.sendEmptyMessage(message); 
	}

}


RTSPClient的tryConnection方法完成了和服务器的RTSP请求交互:

/*
 * Copyright (C) 2011-2014 GUIGUI Simon, fyhertz@gmail.com
 * 
 * This file is part of Spydroid (http://code.google.com/p/spydroid-ipcamera/)
 * 
 * Spydroid is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This source code is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this source code; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

package net.majorkernelpanic.streaming.rtsp;

import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.util.Log;

import net.majorkernelpanic.streaming.Session;
import net.majorkernelpanic.streaming.Stream;
import net.majorkernelpanic.streaming.video.VideoStream;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.Socket;
import java.net.SocketException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.Locale;
import java.util.concurrent.Semaphore;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * RFC 2326.
 * A basic and asynchronous RTSP client.
 * The original purpose of this class was to implement a small RTSP client compatible with Wowza.
 * It implements Digest Access Authentication according to RFC 2069. 
 */
public class RtspClient {

	public final static String TAG = "RtspClient";

	/** Message sent when the connection to the RTSP server failed. */
	public final static int ERROR_CONNECTION_FAILED = 0x01;
	
	/** Message sent when the credentials are wrong. */
	public final static int ERROR_WRONG_CREDENTIALS = 0x03;
	
	/** 
	 * Message sent when the connection with the RTSP server has been lost for 
	 * some reason (for example, the user is going under a bridge).
	 * When the connection with the server is lost, the client will automatically try to
	 * reconnect as long as {@link #stopStream()} is not called. 
	 **/
	public final static int ERROR_CONNECTION_LOST = 0x04;
	
	/**
	 * Message sent when the connection with the RTSP server has been reestablished.
	 * When the connection with the server is lost, the client will automatically try to
	 * reconnect as long as {@link #stopStream()} is not called.
	 */
	public final static int MESSAGE_CONNECTION_RECOVERED = 0x05;

	private final static int STATE_STARTED = 0x00;
	private final static int STATE_STARTING = 0x01;
	private final static int STATE_STOPPING = 0x02;
	private final static int STATE_STOPPED = 0x03;
	private int mState = 0;
	private int mPort = 0;

	private class Parameters {
		public String host; 
		public String username;
		public String password;
		public String path;
		public Session session;
		public int port;
		
		public Parameters clone() {
			Parameters params = new Parameters();
			params.host = host;
			params.username = username;
			params.password = password;
			params.path = path;
			params.session = session;
			params.port = port;
			return params;
		}
	}
	
	
	private Parameters mTmpParameters;
	private Parameters mParameters;
	
	private Socket mSocket;
	private String mSessionID;
	private String mAuthorization;
	private BufferedReader mBufferedReader;
	private OutputStream mOutputStream;
	private int mCSeq;
	private Callback mCallback;
	private Handler mMainHandler;
	private Handler mHandler;

	/**
	 * The callback interface you need to implement to know what's going on with the 
	 * RTSP server (for example your Wowza Media Server).
	 */
	public interface Callback {
		public void onRtspUpdate(int message, Exception exception);
	}

	public RtspClient() {
		mCSeq = 0;
		mTmpParameters = new Parameters();
		mTmpParameters.port = 1935;
		mTmpParameters.path = "/";
		setCredentials("1102", "123456");
		mAuthorization = null;
		mCallback = null;
		mMainHandler = new Handler(Looper.getMainLooper());
		mState = STATE_STOPPED;

		final Semaphore signal = new Semaphore(0);
		new HandlerThread("net.majorkernelpanic.streaming.RtspClient"){
			@Override
			protected void onLooperPrepared() {
				mHandler = new Handler();
				signal.release();
			}
		}.start();
		signal.acquireUninterruptibly();
		
	}

	/**
	 * Sets the callback interface that will be called on status updates of the connection
	 * with the RTSP server.
	 * @param cb The implementation of the {@link Callback} interface
	 */
	public void setCallback(Callback cb) {
		mCallback = cb;
	}

	/**
	 * The {@link Session} that will be used to stream to the server.
	 * If not called before {@link #startStream()}, a it will be created.
	 */
	public void setSession(Session session) {
		mTmpParameters.session = session;
	}

	public Session getSession() {
		return mTmpParameters.session;
	}	

	/**
	 * Sets the destination address of the RTSP server.
	 * @param host The destination address
	 * @param port The destination port
	 */
	public void setServerAddress(String host, int port) {
		mTmpParameters.port = port;
		mTmpParameters.host = host;
	}

	/**
	 * If authentication is enabled on the server, you need to call this with a valid username/password pair.
	 * Only implements Digest Access Authentication according to RFC 2069.
	 * @param username The username
	 * @param password The password
	 */
	public void setCredentials(String username, String password) {
		mTmpParameters.username = username;
		mTmpParameters.password = password;
	}

	/**
	 * The path to which the stream will be sent to. 
	 * @param path The path
	 */
	public void setStreamPath(String path) {
		mTmpParameters.path = path;
	}

	public boolean isStreaming() {
		return mState==STATE_STARTED|mState==STATE_STARTING;
	}

	/**
	 * Connects to the RTSP server to publish the stream, and the effectively starts streaming.
	 * You need to call {@link #setServerAddress(String, int)} and optionnally {@link #setSession(Session)} 
	 * and {@link #setCredentials(String, String)} before calling this.
	 * Should be called of the main thread !
	 */
	public void startStream(int port) {
		mPort = port;
		if (mTmpParameters.host == null) throw new IllegalStateException("setServerAddress(String,int) has not been called !");
		if (mTmpParameters.session == null) throw new IllegalStateException("setSession() has not been called !");
		mHandler.post(new Runnable () {
			@Override
			public void run() {
				if (mState != STATE_STOPPED) return;
				mState = STATE_STARTING;
				
				Log.i(TAG,"Connecting to RTSP server...");
				
				// If the user calls some methods to configure the client, it won't modify its behavior until the stream is restarted
				mParameters = mTmpParameters.clone();
				mParameters.session.setDestination(mTmpParameters.host);
				
				try {
					mParameters.session.syncConfigure();
				} catch (Exception e) {
					mParameters.session = null;
					mState = STATE_STOPPED;
					return;
				}				
				
				try {
					tryConnection();
					 
				} catch (Exception e) {
//					postError(ERROR_CONNECTION_FAILED);
					Log.i(TAG,"Exception failed:"+e.getMessage());
//					abord();
					return;
				}

				try {
					mParameters.session.syncStart();
					mState = STATE_STARTED;
					
					
//					mHandler.post(mConnectionMonitor);
				} catch (Exception e) {
					Log.i(TAG,"ii Exception failed:"+e.getMessage());
//					abord();
				}
				//notify user success.
				postMessage(0); 
			}
		});

	}
	
	public void switchCamera(){

		Stream stream = mParameters.session.getTrack(1);//video
		if (stream instanceof VideoStream){
			VideoStream videoStream = (VideoStream)stream;
			try {
				videoStream.switchCamera();
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}

	public void setFlash(boolean bFlag){
		Stream stream = mParameters.session.getTrack(1);//video
		if (stream instanceof VideoStream){
			VideoStream videoStream = (VideoStream)stream;
			try {
				videoStream.setFlashState(bFlag);
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		
	}
	
	/**
	 * Stops the stream, and informs the RTSP server.
	 */
	public void stopStream() {
		mHandler.post(new Runnable () {
			@Override
			public void run() {
				if (mParameters != null && mParameters.session != null) {
					mParameters.session.stop();
				}
				if (mState != STATE_STOPPED) {
					mState = STATE_STOPPING;
					abord();
				}
			}
		});
	}

	public void release() {
		stopStream();
		mHandler.getLooper().quit();
	}
	
	private void abord() {
		Log.i(TAG, "abord");
		try {
			sendRequestTeardown();
		} catch (Exception ignore) {}
		try {
			mSocket.close();
		} catch (Exception ignore) {}
		mHandler.removeCallbacks(mConnectionMonitor);
		mHandler.removeCallbacks(mRetryConnection);
		mState = STATE_STOPPED; 
	}
	
	private void tryConnection() throws IOException {
		mCSeq = 0;
		mSocket = new Socket(mParameters.host, mParameters.port);
		mBufferedReader = new BufferedReader(new InputStreamReader(mSocket.getInputStream()));
		mOutputStream = mSocket.getOutputStream();
		sendRequestOption();
		sendRequestAnnounce();
		if (sendRequestSetup()){
			if (!sendRequestRecord()){
				Log.e(TAG, "Record failed.");
				postError(ERROR_CONNECTION_FAILED);
			}
		}else{
			postError(ERROR_CONNECTION_FAILED);
		}
	}
	
	/**
	 * Forges and sends the ANNOUNCE request 
	 */
	private void sendRequestAnnounce() throws IllegalStateException, SocketException, IOException {

		String body = mParameters.session.getSessionDescription();
		String request = "ANNOUNCE rtsp://"+mParameters.host+":"+mParameters.port+mParameters.path+" RTSP/1.0\r\n" +
				"Content-Type: application/sdp\r\n" +
				"CSeq: " + (++mCSeq) + "\r\n" +
				"User-Agent: " + "XdjaClient" + "\r\n" +
				"Content-Length: " + body.length() + "\r\n\r\n" +
				body; 
		Log.i(TAG,request.substring(0, request.indexOf("\r\n")));

		mOutputStream.write(request.getBytes("UTF-8"));
		Response response = Response.parseResponse(mBufferedReader);

		if (response.headers.containsKey("server")) {
			Log.i(TAG,"RTSP server name:" + response.headers.get("server"));
		} else {
			Log.i(TAG,"RTSP server name unknown");
		}
//
//		try {
//			Matcher m = Response.rexegSession.matcher(response.headers.get("session"));
//			m.find();
//			mSessionID = m.group(1);
//		} catch (Exception e) {
//			throw new IOException("Invalid response from server. Session id: "+mSessionID);
//		}

		if (response.status == 401) {
			String nonce, realm;
			Matcher m;

			if (mParameters.username == null || mParameters.password == null) throw new IllegalStateException("Authentication is enabled and setCredentials(String,String) was not called !");

			try {
				m = Response.rexegAuthenticate.matcher(response.headers.get("www-authenticate")); m.find();
				nonce = m.group(2);
				realm = m.group(1);
			} catch (Exception e) {
				throw new IOException("Invalid response from server");
			}

			String uri = "rtsp://"+mParameters.host+":"+mParameters.port+mParameters.path;
			String hash1 = computeMd5Hash(mParameters.username+":"+m.group(1)+":"+mParameters.password);
			String hash2 = computeMd5Hash("ANNOUNCE"+":"+uri);
			String hash3 = computeMd5Hash(hash1+":"+m.group(2)+":"+hash2);

			mAuthorization = "Digest username=\""+mParameters.username+"\",realm=\""+realm+"\",nonce=\""+nonce+"\",uri=\""+uri+"\",response=\""+hash3+"\"\r\n";

			request = "ANNOUNCE rtsp://"+mParameters.host+":"+mParameters.port+mParameters.path+" RTSP/1.0\r\n" +
					"CSeq: " + (++mCSeq) + "\r\n" +
					"Content-Type: application/sdp"+ "\r\n" +
					"Content-Length: " + body.length() + "\r\n" +
					"Authorization: " + mAuthorization +
					"Session: " + mSessionID + "\r\n" +
					"User-Agent: " + "xdja-xa" + "\r\n\r\n" +
					body+ "\r\n\r\n";

			Log.i(TAG,request);

			mOutputStream.write(request.getBytes("UTF-8"));
			response = Response.parseResponse(mBufferedReader);

			if (response.status == 401) throw new RuntimeException("Bad credentials !");

		} else if (response.status == 403) {
			throw new RuntimeException("Access forbidden !");
		}

	}

	/**
	 * Forges and sends the SETUP request 
	 */
	private boolean sendRequestSetup() throws IllegalStateException, SocketException, IOException {
		boolean bHaveAudio = false;
		for (int i=0;i<2;i++) {
			Stream stream = mParameters.session.getTrack(i);
			if (stream != null) {
				if (i == 0){
					bHaveAudio = true;
				}
			/*	String request = "SETUP rtsp://"+mParameters.host+":"+mParameters.port+mParameters.path+"/trackID="+i+" RTSP/1.0\r\n" +
						"Transport: RTP/AVP/UDP;unicast;client_port="+(5000+2*i)+"-"+(5000+2*i+1)+";mode=record\r\n" +
						addHeaders();*/
				int trackId = i;
				if (!bHaveAudio){
					trackId = 0;
				}
				String request = "SETUP rtsp://"+mParameters.host+":"+mParameters.port+mParameters.path+"/streamid="+trackId+" RTSP/1.0\r\n" +
						"Transport: RTP/AVP/UDP;unicast;client_port="+(mPort+2*i)+"-"+(mPort+2*i+1)+";mode=record\r\n" +
						addHeaders();
 
				Log.i(TAG,request.substring(0, request.indexOf("\r\n")));

				mOutputStream.write(request.getBytes("UTF-8"));
				Response response = Response.parseResponse(mBufferedReader);//
				if (i == 0){
					try { 
						mSessionID = response.headers.get("session").trim();
						Log.i(TAG,"mSessionID: "+ mSessionID+ "response.status:"+response.status);
					} catch (Exception e) {
						throw new IOException("Invalid response from server. Session id: "+mSessionID);
					}
				}
				
				if (response.status != 200){
					Log.i(TAG,"return for resp :" +response.status);
					return false;
				}
				Matcher m;
				try {
					if (response.headers.get("transport") == null){
						Log.i(TAG,"return for not transport");
						return false;
					}
					m = Response.rexegTransport.matcher(response.headers.get("transport")); m.find();
					stream.setDestinationPorts(Integer.parseInt(m.group(3)), Integer.parseInt(m.group(4)));
//					mParameters.session.syncStart(i);
					Log.i(TAG, "Setting destination ports: "+Integer.parseInt(m.group(3))+", "+Integer.parseInt(m.group(4)));
				} catch (Exception e) {
					e.printStackTrace();
					int[] ports = stream.getDestinationPorts();
					Log.i(TAG,"Server did not specify ports, using default ports: "+ports[0]+"-"+ports[1]);
				}
			} 
		}

		return true;
	}

	/**
	 * Forges and sends the RECORD request 
	 */
	private boolean sendRequestRecord() throws IllegalStateException, SocketException, IOException {
		String request = "RECORD rtsp://"+mParameters.host+":"+mParameters.port+mParameters.path+" RTSP/1.0\r\n" +
				"Range: npt=0.000-\r\n" +
				addHeaders();
		Log.i(TAG,request.substring(0, request.indexOf("\r\n")));
		mOutputStream.write(request.getBytes("UTF-8"));
		Response resp = Response.parseResponse(mBufferedReader);
		if (resp.status != 200){
			return false;
		}
		
		return true;
	}

	/**
	 * Forges and sends the TEARDOWN request 
	 */
	private void sendRequestTeardown() throws IOException {
		String request = "TEARDOWN rtsp://"+mParameters.host+":"+mParameters.port+mParameters.path+" RTSP/1.0\r\n" + addHeaders();
		Log.i(TAG,request.substring(0, request.indexOf("\r\n")));
		mOutputStream.write(request.getBytes("UTF-8"));
	}
	
	/**
	 * Forges and sends the OPTIONS request 
	 */
	private void sendRequestOption() throws IOException {
		String request = "OPTIONS rtsp://"+mParameters.host+":"+mParameters.port+mParameters.path+" RTSP/1.0\r\n" + addHeaders();
		Log.i(TAG,request.substring(0, request.indexOf("\r\n")));
		mOutputStream.write(request.getBytes("UTF-8"));
		Response.parseResponse(mBufferedReader);
	}	

	private String addHeaders() {
		return "CSeq: " + (++mCSeq) + "\r\n" +
				"Content-Length: 0\r\n" +
				(mSessionID != null ? "Session: " + mSessionID + "\r\n" :"") +
				"User-Agent: " + "xdja-xa" + "\r\n" +
				(mAuthorization != null ? "Authorization: " + mAuthorization +  "\r\n\r\n":"\r\n");
	}	 

	/**
	 * If the connection with the RTSP server is lost, we try to reconnect to it as
	 * long as {@link #stopStream()} is not called.
	 */
	private Runnable mConnectionMonitor = new Runnable() {
		@Override
		public void run() {
			if (mState == STATE_STARTED) {
				try {
					// We poll the RTSP server with OPTION requests
					sendRequestOption();
					mHandler.postDelayed(mConnectionMonitor, 6000);
				} catch (IOException e) {
					// Happens if the OPTION request fails
					postMessage(ERROR_CONNECTION_LOST);
					Log.e(TAG, "Connection lost with the server...");
					mParameters.session.stop();
					mHandler.post(mRetryConnection);
				}
			}
		}
	};

	/** Here, we try to reconnect to the RTSP. */
	private Runnable mRetryConnection = new Runnable() {
		@Override
		public void run() {
			if (mState == STATE_STARTED) {
				try {
					Log.e(TAG, "Trying to reconnect...");
					tryConnection();
					try {
						mParameters.session.start();
						mHandler.post(mConnectionMonitor);
						postMessage(MESSAGE_CONNECTION_RECOVERED);
					} catch (Exception e) {
						abord();
					}
				} catch (IOException e) {
					mHandler.postDelayed(mRetryConnection,1000);
				}
			}
		}
	};
	
	final protected static char[] hexArray = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};

	private static String bytesToHex(byte[] bytes) {
		char[] hexChars = new char[bytes.length * 2];
		int v;
		for ( int j = 0; j < bytes.length; j++ ) {
			v = bytes[j] & 0xFF;
			hexChars[j * 2] = hexArray[v >>> 4];
			hexChars[j * 2 + 1] = hexArray[v & 0x0F];
		}
		return new String(hexChars);
	}

	/** Needed for the Digest Access Authentication. */
	private String computeMd5Hash(String buffer) {
		MessageDigest md;
		try {
			md = MessageDigest.getInstance("MD5");
			return bytesToHex(md.digest(buffer.getBytes("UTF-8")));
		} catch (NoSuchAlgorithmException ignore) {
		} catch (UnsupportedEncodingException e) {}
		return "";
	}

	private void postMessage(final int message) {
		mMainHandler.post(new Runnable() {
			@Override
			public void run() {
				if (mCallback != null) {
					mCallback.onRtspUpdate(message, null); 
				}
			}
		});
	}

	private void postError(final int message) {
		mMainHandler.post(new Runnable() {
			@Override
			public void run() {
				if (mCallback != null) {
					mCallback.onRtspUpdate(message, null); 
				}
			}
		});
	}	

	static class Response {

		// Parses method & uri
		public static final Pattern regexStatus = Pattern.compile("RTSP/\\d.\\d (\\d+) (\\w+)",Pattern.CASE_INSENSITIVE);
		// Parses a request header
		public static final Pattern rexegHeader = Pattern.compile("(\\S+):(.+)",Pattern.CASE_INSENSITIVE);
		// Parses a WWW-Authenticate header
		public static final Pattern rexegAuthenticate = Pattern.compile("realm=\"(.+)\",\\s+nonce=\"(\\w+)\"",Pattern.CASE_INSENSITIVE);
		// Parses a Session header
		public static final Pattern rexegSession = Pattern.compile("(\\d+)",Pattern.CASE_INSENSITIVE);
		// Parses a Transport header
		public static final Pattern rexegTransport = Pattern.compile("client_port=(\\d+)-(\\d+).+server_port=(\\d+)-(\\d+)",Pattern.CASE_INSENSITIVE);


		public int status;
		public HashMap<String,String> headers = new HashMap<String,String>();

		/** Parse the method, uri & headers of a RTSP request */
		public static Response parseResponse(BufferedReader input) throws IOException, IllegalStateException, SocketException {
			Response response = new Response();
			String line;
			Matcher matcher;
			// Parsing request method & uri
			if ((line = input.readLine())==null) throw new SocketException("Connection lost");
			matcher = regexStatus.matcher(line);
			matcher.find();
			response.status = Integer.parseInt(matcher.group(1));

			// Parsing headers of the request
			while ( (line = input.readLine()) != null) {
				//Log.e(TAG,"l: "+line.length()+"c: "+line);
				if (line.length()>3) {
					matcher = rexegHeader.matcher(line);
					matcher.find();
					Log.i(TAG, "response.headers: "+matcher.group(1).toLowerCase(Locale.US)+": "+ matcher.group(2));
					response.headers.put(matcher.group(1).toLowerCase(Locale.US), matcher.group(2));
				} else {
					break;
				}
			}
			if (line==null) throw new SocketException("Connection lost");

			Log.i(TAG, "Response from server: "+response.status);

			return response;
		}
	}

}


界面部分只需要实例化一个RtspPushStreamClient对象,并调用startRtspClient方法就可以启动推流动作,如果是视频流,则需要添加一个net.majorkernelpanic.streaming.gl.SurfaceView 的SurfaceView,并通过SessionBuilder.getInstance().setSurfaceView设置到spydroid提供的接口中。



  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 11
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 11
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值