├── .gitattributes ├── .gitignore ├── README.md └── rtspclient ├── RtspClient.java └── RtspClinet ├── Audio ├── ACCStream.java └── AudioStream.java ├── Socket ├── RtcpSocket.java └── RtpSocket.java ├── Stream └── RtpStream.java └── Video ├── H264Stream.java └── VideoStream.java /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Built application files 2 | *.apk 3 | *.ap_ 4 | 5 | # Files for the Dalvik VM 6 | *.dex 7 | 8 | # Java class files 9 | *.class 10 | 11 | # Generated files 12 | bin/ 13 | gen/ 14 | 15 | # Gradle files 16 | .gradle/ 17 | build/ 18 | 19 | # Local configuration file (sdk path, etc) 20 | local.properties 21 | 22 | # Proguard folder generated by Eclipse 23 | proguard/ 24 | 25 | # Log Files 26 | *.log 27 | 28 | # ========================= 29 | # Operating System Files 30 | # ========================= 31 | 32 | # OSX 33 | # ========================= 34 | 35 | .DS_Store 36 | .AppleDouble 37 | .LSOverride 38 | 39 | # Thumbnails 40 | ._* 41 | 42 | # Files that might appear on external disk 43 | .Spotlight-V100 44 | .Trashes 45 | 46 | # Directories potentially created on remote AFP share 47 | .AppleDB 48 | .AppleDesktop 49 | Network Trash Folder 50 | Temporary Items 51 | .apdisk 52 | 53 | # Windows 54 | # ========================= 55 | 56 | # Windows image file caches 57 | Thumbs.db 58 | ehthumbs.db 59 | 60 | # Folder config file 61 | Desktop.ini 62 | 63 | # Recycle Bin used on file shares 64 | $RECYCLE.BIN/ 65 | 66 | # Windows Installer files 67 | *.cab 68 | *.msi 69 | *.msm 70 | *.msp 71 | 72 | # Windows shortcuts 73 | *.lnk 74 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RtspClient 2 | 3 | 4 | > - [x] **实现基本框架** 5 | > - [x] **支持以UDP方式接收RTP报文** 6 | - [ ] 优化UDP报文解码 7 | > - [x] **支持以TCP方式接收RTP报文** 8 | - [ ] 优化TCP报文重组 9 | > - [x] **实现RTP报文重组** 10 | - [x] 支持FU-A报文重组 11 | - [x] 支持单包NAL报文 12 | - [ ] 增加常用NAL报文重组 13 | > - [x] **支持H264解码显示** 14 | - [x] 支持H264硬解码显示(Android 4.1以上版本支持) 15 | - [ ] 支持H264软解码显示 16 | > - [ ] **支持ACC音频** 17 | 18 | ---- 19 | 20 | #### 使用方法 21 | 22 | 1. 使用Jcenter仓库 23 | > compile "com.aaronhan:rtspclient:0.7" 24 | 25 | 2. 调用RtspClient方法 26 | ```java 27 | private SurfaceView mSurfaceView; 28 | 29 | //创建client,需要传入一个SurfaceView作为显示 30 | String host = "rtsp://192.168.0.217/test.264" 31 | RtspClient mRtspClient = new RtspClient(host); 32 | mRtspClient.setSurfaceView(mSurfaceView); 33 | 34 | //开始显示 35 | mRtspClient.start(); 36 | 37 | //关闭,请在Activity销毁时调用此方法 38 | //在UDP模式下即使销毁Activity某些RTSP服务器也会继续发送报文 39 | mRtspClient.shutdown(); 40 | ``` 41 | 3.RtspClient调用详解 42 | ```java 43 | //使用RTP传输协议选择,支持"tcp"和"udp"传入值 44 | String method = "udp"; 45 | 46 | //传入地址,需以rtsp://开头,如果只有IP,需以/结尾,如rtsp://xxx.xxx.xxx.xxx/ 47 | //支持地址后加入端口地址,"rtsp://ip:port/xxx" 48 | //如未加入端口地址,则使用默认地址554 49 | String host = "rtsp://192.168.0.217/test.264" 50 | 51 | //可以单独传入port值,不在地址中增加 52 | //如未传入port只,且地址中没有port,默认使用554 53 | int port = 8554; 54 | 55 | //支持传入用户名密码,某些RTSP服务器需要认证使用 56 | String username = "admin"; 57 | String password = "admin"; 58 | 59 | //只传入地址或地址加端口 60 | //默认无用户名密码认证,默认使用udp协议 61 | RtspClient(host); 62 | RtspClient(host,port); 63 | 64 | //传入使用协议 65 | RtspClient(method,host); 66 | RtspClient(method,host,port); 67 | 68 | //默认是udp协议,传入认证用户名和密码 69 | RtspClient(host,username,password); 70 | RtspClient(host,username,password,port); 71 | 72 | //传入使用协议和认证信息 73 | RtspClient(method,host,username,password); 74 | RtspClient(method,host,username,password,port); 75 | ``` 76 | -------------------------------------------------------------------------------- /rtspclient/RtspClient.java: -------------------------------------------------------------------------------- 1 | package com.aaronhan.rtspclient; 2 | 3 | import android.os.Handler; 4 | import android.os.HandlerThread; 5 | import android.util.Base64; 6 | import android.util.Log; 7 | import android.view.SurfaceView; 8 | 9 | import com.aaronhan.rtspclient.RtspClinet.Video.H264Stream; 10 | import com.aaronhan.rtspclient.RtspClinet.Socket.RtpSocket; 11 | 12 | import java.io.BufferedReader; 13 | import java.io.IOException; 14 | import java.io.InputStreamReader; 15 | import java.io.OutputStream; 16 | import java.io.UnsupportedEncodingException; 17 | import java.net.Socket; 18 | import java.util.HashMap; 19 | import java.util.Locale; 20 | import java.util.concurrent.Semaphore; 21 | import java.util.regex.Matcher; 22 | import java.util.regex.Pattern; 23 | 24 | /** 25 | * 26 | */ 27 | public class RtspClient { 28 | 29 | private final static String tag = "ComfdRtspClient"; 30 | private final static String UserAgent = "Rtsp/0.1"; 31 | private final static int STATE_STARTED = 0x00; 32 | private final static int STATE_STARTING = 0x01; 33 | private final static int STATE_STOPPING = 0x02; 34 | private final static int STATE_STOPPED = 0x03; 35 | private final static String METHOD_UDP = "udp"; 36 | private final static String METHOD_TCP = "tcp"; 37 | private final static int TRACK_VIDEO = 0x01; 38 | private final static int TRACK_AUDIO = 0x02; 39 | 40 | private class Parameters { 41 | public String host; 42 | public String address; 43 | public int port; 44 | public int rtpPort; 45 | public int serverPort; 46 | } 47 | 48 | public class SDPInfo { 49 | public boolean audioTrackFlag; 50 | public boolean videoTrackFlag; 51 | public String videoTrack; 52 | public String audioTrack; 53 | public String SPS; 54 | public String PPS; 55 | public int packetizationMode; 56 | } 57 | 58 | private Socket mSocket; 59 | private BufferedReader mBufferreader; 60 | private OutputStream mOutputStream; 61 | private Parameters mParams; 62 | private Handler mHandler; 63 | private int CSeq; 64 | private int mState; 65 | private String mSession; 66 | private RtpSocket mRtpSocket; 67 | private boolean isTCPtranslate; 68 | private static boolean Describeflag = false; //used to get SDP info 69 | private static SDPInfo sdpInfo; 70 | private String authorName, authorPassword, authorBase64; 71 | private HandlerThread thread; 72 | 73 | private H264Stream mH264Stream; 74 | 75 | private SurfaceView mSurfaceView; 76 | 77 | public RtspClient(String address,String name, String password) { 78 | this("udp",address,name,password); 79 | } 80 | 81 | public RtspClient(String address, String name ,String password, int port) { 82 | this("udp",address,name,password,port); 83 | } 84 | 85 | public RtspClient(String address) { 86 | this("udp",address,null,null); 87 | } 88 | 89 | public RtspClient(String method, String address) { 90 | this(method,address,null,null); 91 | } 92 | 93 | public RtspClient(String method, String address, int port) { 94 | this(method,address,null,null,port); 95 | } 96 | 97 | public RtspClient(String address, int port) { 98 | this("udp",address,null,null,port); 99 | } 100 | 101 | public RtspClient(String method, String address,String name,String password) { 102 | String url = address.substring(address.indexOf("//") + 2); 103 | url = url.substring(0,url.indexOf("/")); 104 | String[] tmp = url.split(":"); 105 | Log.d(tag, url); 106 | authorName = name; 107 | authorPassword = password; 108 | if(tmp.length == 1) ClientConfig(tmp[0], address, 554); 109 | else if(tmp.length == 2) ClientConfig(tmp[0], address, Integer.parseInt(tmp[1])); 110 | if( method.equalsIgnoreCase(METHOD_UDP) ) { 111 | isTCPtranslate = false; 112 | } else if( method.equalsIgnoreCase(METHOD_TCP)) { 113 | isTCPtranslate = true; 114 | } 115 | } 116 | 117 | public RtspClient(String method, String address,String name,String password, int port) { 118 | String url = address.substring(address.indexOf("//") + 2); 119 | url = url.substring(0,url.indexOf("/")); 120 | authorName = name; 121 | authorPassword = password; 122 | ClientConfig(url, address, port); 123 | if( method.equalsIgnoreCase(METHOD_UDP) ) { 124 | isTCPtranslate = false; 125 | } else if( method.equalsIgnoreCase(METHOD_TCP)) { 126 | isTCPtranslate = true; 127 | } 128 | } 129 | 130 | public void setSurfaceView( SurfaceView s ) { 131 | mSurfaceView = s; 132 | } 133 | 134 | private void ClientConfig(String host, String address, int port) { 135 | mParams = new Parameters(); 136 | sdpInfo = new SDPInfo(); 137 | mParams.host = host; 138 | mParams.port = port; 139 | mParams.address = address.substring(7); 140 | CSeq = 0; 141 | mState = STATE_STOPPED; 142 | mSession = null; 143 | if(authorName == null && authorPassword == null) { 144 | authorBase64 = null; 145 | } 146 | else { 147 | authorBase64 = Base64.encodeToString((authorName+":"+authorPassword).getBytes(),Base64.DEFAULT); 148 | } 149 | 150 | final Semaphore signal = new Semaphore(0); 151 | thread = new HandlerThread("RTSPCilentThread") { 152 | protected void onLooperPrepared() { 153 | mHandler = new Handler(); 154 | signal.release(); 155 | } 156 | }; 157 | thread.start(); 158 | signal.acquireUninterruptibly(); 159 | } 160 | 161 | public void start() { 162 | mHandler.post(startConnection); 163 | } 164 | 165 | private Runnable startConnection = new Runnable() { 166 | @Override 167 | public void run() { 168 | if (mState != STATE_STOPPED) return; 169 | mState = STATE_STARTING; 170 | 171 | Log.d(tag, "Start to connect the server..."); 172 | 173 | try { 174 | tryConnection(); 175 | mHandler.post(sendGetParameter); 176 | } catch (IOException e) { 177 | Log.e(tag, e.toString()); 178 | abort(); 179 | } 180 | } 181 | }; 182 | 183 | private Runnable sendGetParameter = new Runnable() { 184 | @Override 185 | public void run() { 186 | try { 187 | sendRequestGetParameter(); 188 | mHandler.postDelayed(sendGetParameter,55000); 189 | } catch (IOException e) { 190 | e.printStackTrace(); 191 | } 192 | } 193 | }; 194 | 195 | public void abort() { 196 | try { 197 | if(mState == STATE_STARTED) sendRequestTeardown(); 198 | } catch ( IOException e ) {} 199 | try { 200 | if(mSocket!=null) mSocket.close(); 201 | } catch ( IOException e ) {} 202 | mState = STATE_STOPPED; 203 | mHandler.removeCallbacks(startConnection); 204 | mHandler.removeCallbacks(sendGetParameter); 205 | } 206 | 207 | public void shutdown(){ 208 | if(mState == STATE_STARTED || 209 | mState == STATE_STARTING) { 210 | mHandler.removeCallbacks(startConnection); 211 | mHandler.removeCallbacks(sendGetParameter); 212 | try { 213 | if(mH264Stream!=null) { 214 | mH264Stream.stop(); 215 | mH264Stream = null; 216 | } 217 | if(mRtpSocket!=null) { 218 | mRtpSocket.stop(); 219 | mRtpSocket = null; 220 | } 221 | if(mState == STATE_STARTED) sendRequestTeardown(); 222 | if(mSocket!=null) { 223 | mSocket.close(); 224 | mSocket = null; 225 | } 226 | mState = STATE_STOPPED; 227 | thread.quit(); 228 | } catch (IOException e) { 229 | e.printStackTrace(); 230 | } 231 | } 232 | } 233 | 234 | public boolean isStarted() { 235 | return mState == STATE_STARTED | mState == STATE_STARTING; 236 | } 237 | 238 | private void tryConnection () throws IOException { 239 | mSocket = new Socket(mParams.host, mParams.port); 240 | mBufferreader = new BufferedReader(new InputStreamReader(mSocket.getInputStream())); 241 | mOutputStream = mSocket.getOutputStream(); 242 | mState = STATE_STARTING; 243 | sendRequestOptions(); 244 | sendRequestDescribe(); 245 | sendRequestSetup(); 246 | sendRequestPlay(); 247 | } 248 | 249 | private void sendRequestOptions() throws IOException { 250 | String request = "OPTIONS rtsp://" + mParams.address + " RTSP/1.0\r\n" + addHeaders(); 251 | Log.d(tag, request.substring(0, request.indexOf("\r\n"))); 252 | mOutputStream.write(request.getBytes("UTF-8")); 253 | Response.parseResponse(mBufferreader); 254 | } 255 | 256 | private void sendRequestDescribe() throws IOException { 257 | String request = "DESCRIBE rtsp://" + mParams.address + " RTSP/1.0\r\n" + addHeaders(); 258 | Log.d(tag, request.substring(0, request.indexOf("\r\n"))); 259 | Describeflag = true; 260 | mOutputStream.write(request.getBytes("UTF-8")); 261 | Response.parseResponse(mBufferreader); 262 | } 263 | 264 | private void sendRequestSetup() throws IOException { 265 | Matcher matcher; 266 | String request; 267 | if ( isTCPtranslate ) { 268 | request = "SETUP rtsp://" + mParams.address + "/" + sdpInfo.videoTrack + " RTSP/1.0\r\n" 269 | + "Transport: RTP/AVP/TCP;unicast;client_port=55640-55641" + "\r\n" 270 | + addHeaders(); 271 | } else { 272 | request = "SETUP rtsp://" + mParams.address + "/" + sdpInfo.videoTrack +" RTSP/1.0\r\n" 273 | + "Transport: RTP/AVP/UDP;unicast;client_port=55640-55641" + "\r\n" 274 | + addHeaders(); 275 | } 276 | Log.d(tag, request.substring(0, request.indexOf("\r\n"))); 277 | mOutputStream.write(request.getBytes("UTF-8")); 278 | Response mResponse = Response.parseResponse(mBufferreader); 279 | 280 | //there has two different session type, one is without timeout , another is with timeout 281 | matcher = Response.regexSessionWithTimeout.matcher(mResponse.headers.get("session")); 282 | if(matcher.find()) mSession = matcher.group(1); 283 | else mSession = mResponse.headers.get("session"); 284 | Log.d(tag,"the session is " + mSession); 285 | 286 | //get the port information and start the RTP socket, ready to receive data 287 | if(isTCPtranslate) matcher = Response.regexTCPTransport.matcher(mResponse.headers.get("transport")); 288 | else matcher = Response.regexUDPTransport.matcher(mResponse.headers.get("transport")); 289 | if(matcher.find()) { 290 | Log.d(tag, "The client port is:" + matcher.group(1) + " ,the server prot is:" + (isTCPtranslate?"null":matcher.group(2)) + "..."); 291 | mParams.rtpPort = Integer.parseInt(matcher.group(1)); 292 | if(!isTCPtranslate) mParams.serverPort = Integer.parseInt(matcher.group(2)); 293 | 294 | //prepare for the video decoder 295 | mH264Stream = new H264Stream(sdpInfo); 296 | mH264Stream.setSurfaceView(mSurfaceView); 297 | 298 | if(isTCPtranslate) mRtpSocket = new RtpSocket(isTCPtranslate, mParams.rtpPort, mParams.host, -1,TRACK_VIDEO); 299 | else mRtpSocket = new RtpSocket(isTCPtranslate, mParams.rtpPort, mParams.host, mParams.serverPort,TRACK_VIDEO); 300 | mRtpSocket.startRtpSocket(); 301 | mRtpSocket.setStream(mH264Stream); 302 | } else { 303 | if(isTCPtranslate) { 304 | Log.d(tag,"Without get the transport port infom, use the rtsp tcp socket!"); 305 | mParams.rtpPort = mParams.port; 306 | 307 | //prepare for the video decoder 308 | mH264Stream = new H264Stream(sdpInfo); 309 | mH264Stream.setSurfaceView(mSurfaceView); 310 | 311 | mRtpSocket = new RtpSocket(isTCPtranslate,mParams.rtpPort,mParams.host,-2,TRACK_VIDEO); 312 | mRtpSocket.setRtspSocket(mSocket); 313 | mRtpSocket.startRtpSocket(); 314 | mRtpSocket.setStream(mH264Stream); 315 | mState = STATE_STARTED; 316 | } 317 | } 318 | } 319 | 320 | private void sendRequestPlay() throws IOException { 321 | String request = "PLAY rtsp://" + mParams.address + " RTSP/1.0\r\n" 322 | + "Range: npt=0.000-\r\n" 323 | + addHeaders(); 324 | Log.d(tag, request.substring(0, request.indexOf("\r\n"))); 325 | mOutputStream.write(request.getBytes("UTF-8")); 326 | Response.parseResponse(mBufferreader); 327 | } 328 | 329 | private void sendRequestTeardown() throws IOException { 330 | String request = "TEARDOWN rtsp://" + mParams.address + "/" + sdpInfo.videoTrack + " RTSP/1.0\r\n" + addHeaders(); 331 | Log.d(tag, request.substring(0, request.indexOf("\r\n"))); 332 | mOutputStream.write(request.getBytes("UTF-8")); 333 | mState = STATE_STOPPING; 334 | } 335 | 336 | private void sendRequestGetParameter() throws IOException { 337 | String request = "GET_PARAMETER rtsp://" + mParams.address + "/" + sdpInfo.videoTrack + " RTSP/1.0\r\n" + addHeaders(); 338 | Log.d(tag, request.substring(0, request.indexOf("\r\n"))); 339 | mOutputStream.write(request.getBytes("UTF-8")); 340 | } 341 | 342 | private String addHeaders() { 343 | return "CSeq: " + (++CSeq) + "\r\n" 344 | + ((authorBase64 == null)?"":("Authorization: Basic " +authorBase64 +"\r\n")) 345 | + "User-Agent: " + UserAgent + "\r\n" 346 | + ((mSession == null)?"":("Session: " + mSession + "\r\n")) 347 | + "\r\n"; 348 | } 349 | 350 | static class Response { 351 | 352 | public static final Pattern regexStatus = Pattern.compile("RTSP/\\d.\\d (\\d+) .+",Pattern.CASE_INSENSITIVE); 353 | public static final Pattern regexHeader = Pattern.compile("(\\S+): (.+)",Pattern.CASE_INSENSITIVE); 354 | public static final Pattern regexUDPTransport = Pattern.compile("client_port=(\\d+)-\\d+;server_port=(\\d+)-\\d+",Pattern.CASE_INSENSITIVE); 355 | public static final Pattern regexTCPTransport = Pattern.compile("client_port=(\\d+)-\\d+;",Pattern.CASE_INSENSITIVE); 356 | public static final Pattern regexSessionWithTimeout = Pattern.compile("(\\S+);timeout=(\\d+)",Pattern.CASE_INSENSITIVE); 357 | public static final Pattern regexSDPgetTrack1 = Pattern.compile("trackID=(\\d+)",Pattern.CASE_INSENSITIVE); 358 | public static final Pattern regexSDPgetTrack2 = Pattern.compile("control:(\\S+)",Pattern.CASE_INSENSITIVE); 359 | public static final Pattern regexSDPmediadescript = Pattern.compile("m=(\\S+) .+",Pattern.CASE_INSENSITIVE); 360 | public static final Pattern regexSDPpacketizationMode = Pattern.compile("packetization-mode=(\\d);",Pattern.CASE_INSENSITIVE); 361 | public static final Pattern regexSDPspspps = Pattern.compile("sprop-parameter-sets=(\\S+),(\\S+)",Pattern.CASE_INSENSITIVE); 362 | public static final Pattern regexSDPlength = Pattern.compile("Content-length: (\\d+)",Pattern.CASE_INSENSITIVE); 363 | public static final Pattern regexSDPstartFlag = Pattern.compile("v=(\\d)",Pattern.CASE_INSENSITIVE); 364 | 365 | public int state; 366 | public static HashMap headers = new HashMap<>(); 367 | 368 | public static Response parseResponse(BufferedReader input) throws IOException { 369 | Response response = new Response(); 370 | String line; 371 | Matcher matcher; 372 | int sdpContentLength = 0; 373 | if( (line = input.readLine()) == null) throw new IOException("Connection lost"); 374 | matcher = regexStatus.matcher(line); 375 | if(matcher.find()) 376 | response.state = Integer.parseInt(matcher.group(1)); 377 | else 378 | while ( (line = input.readLine()) != null ) { 379 | matcher = regexStatus.matcher(line); 380 | if(matcher.find()) { 381 | response.state = Integer.parseInt(matcher.group(1)); 382 | break; 383 | } 384 | } 385 | Log.d(tag, "The response state is: "+response.state); 386 | 387 | int foundMediaType = 0; 388 | int sdpHaveReadLength = 0; 389 | boolean sdpStartFlag = false; 390 | 391 | while ( (line = input.readLine()) != null) { 392 | if( line.length() > 3 || Describeflag ) { 393 | Log.d(tag, "The line is: " + line + "..."); 394 | matcher = regexHeader.matcher(line); 395 | if (matcher.find()) 396 | headers.put(matcher.group(1).toLowerCase(Locale.US), matcher.group(2)); //$ to $ 397 | 398 | matcher = regexSDPlength.matcher(line); 399 | if(matcher.find()) { 400 | sdpContentLength = Integer.parseInt(matcher.group(1)); 401 | sdpHaveReadLength = 0; 402 | } 403 | //Here is trying to get the SDP information from the describe response 404 | if (Describeflag) { 405 | matcher = regexSDPmediadescript.matcher(line); 406 | if (matcher.find()) 407 | if (matcher.group(1).equalsIgnoreCase("audio")) { 408 | foundMediaType = 1; 409 | sdpInfo.audioTrackFlag = true; 410 | } else if (matcher.group(1).equalsIgnoreCase("video")) { 411 | foundMediaType = 2; 412 | sdpInfo.videoTrackFlag = true; 413 | } 414 | 415 | matcher = regexSDPpacketizationMode.matcher(line); 416 | if (matcher.find()) { 417 | sdpInfo.packetizationMode = Integer.parseInt(matcher.group(1)); 418 | } 419 | 420 | matcher = regexSDPspspps.matcher(line); 421 | if(matcher.find()) { 422 | sdpInfo.SPS = matcher.group(1); 423 | sdpInfo.PPS = matcher.group(2); 424 | } 425 | 426 | matcher = regexSDPgetTrack1.matcher(line); 427 | if(matcher.find()) 428 | if (foundMediaType == 1) sdpInfo.audioTrack = "trackID=" + matcher.group(1); 429 | else if (foundMediaType == 2) sdpInfo.videoTrack = "trackID=" + matcher.group(1); 430 | 431 | 432 | matcher = regexSDPgetTrack2.matcher(line); 433 | if(matcher.find()) 434 | if (foundMediaType == 1) sdpInfo.audioTrack = matcher.group(1); 435 | else if (foundMediaType == 2) sdpInfo.videoTrack = matcher.group(1); 436 | 437 | 438 | matcher = regexSDPstartFlag.matcher(line); 439 | if(matcher.find()) sdpStartFlag = true; 440 | if(sdpStartFlag) sdpHaveReadLength += line.getBytes().length + 2; 441 | if((sdpContentLength < sdpHaveReadLength + 2) && (sdpContentLength != 0)) { 442 | Describeflag = false; 443 | sdpStartFlag = false; 444 | Log.d(tag, "The SDP info: " 445 | + (sdpInfo.audioTrackFlag ? "have audio info.. " : "haven't the audio info.. ") 446 | + ";" + (sdpInfo.audioTrackFlag ? (" the audio track is " + sdpInfo.audioTrack) : "")); 447 | Log.d(tag, "The SDP info: " 448 | + (sdpInfo.videoTrackFlag ? "have video info.. " : "haven't the vedio info..") 449 | + (sdpInfo.videoTrackFlag ? (" the video track is " + sdpInfo.videoTrack) : "") 450 | + ";" + (sdpInfo.videoTrackFlag ? (" the video SPS is " + sdpInfo.SPS) : "") 451 | + ";" + (sdpInfo.videoTrackFlag ? (" the video PPS is " + sdpInfo.PPS) : "") 452 | + ";" + (sdpInfo.videoTrackFlag ? (" the video packetization mode is " + sdpInfo.packetizationMode) : "")); 453 | break; 454 | } 455 | } 456 | } else { 457 | break; 458 | } 459 | 460 | } 461 | 462 | if( line == null ) throw new IOException("Connection lost"); 463 | 464 | return response; 465 | } 466 | } 467 | } 468 | -------------------------------------------------------------------------------- /rtspclient/RtspClinet/Audio/ACCStream.java: -------------------------------------------------------------------------------- 1 | package com.aaronhan.rtspclient.RtspClinet.Audio; 2 | 3 | /** 4 | * 5 | */ 6 | public class ACCStream extends AudioStream { 7 | 8 | } 9 | 10 | -------------------------------------------------------------------------------- /rtspclient/RtspClinet/Audio/AudioStream.java: -------------------------------------------------------------------------------- 1 | package com.aaronhan.rtspclient.RtspClinet.Audio; 2 | 3 | import android.util.Log; 4 | 5 | import com.aaronhan.rtspclient.RtspClinet.Stream.RtpStream; 6 | 7 | /** 8 | * 9 | */ 10 | public abstract class AudioStream extends RtpStream { 11 | private final static String tag = "AudioStream"; 12 | 13 | protected void recombinePacket(StreamPacks streamPacks) { 14 | 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /rtspclient/RtspClinet/Socket/RtcpSocket.java: -------------------------------------------------------------------------------- 1 | package com.aaronhan.rtspclient.RtspClinet.Socket; 2 | 3 | import android.os.Handler; 4 | import android.os.HandlerThread; 5 | import android.util.Log; 6 | 7 | import java.io.IOException; 8 | import java.net.DatagramPacket; 9 | import java.net.DatagramSocket; 10 | import java.net.InetAddress; 11 | import java.net.SocketException; 12 | import java.net.UnknownHostException; 13 | 14 | /** 15 | *This class is used to send the rtcp packet to the server 16 | */ 17 | public class RtcpSocket { 18 | 19 | private final static String tag = "RtcpSocket"; 20 | 21 | private DatagramSocket mSocket; 22 | private DatagramPacket mPacket; 23 | private Handler mHandler; 24 | private byte[] message = new byte[2048]; 25 | private String serverIp; 26 | private int serverPort; 27 | private boolean isStoped; 28 | private HandlerThread thread; 29 | 30 | public RtcpSocket(int port, String serverIp, int serverPort) { 31 | try { 32 | this.serverIp = serverIp; 33 | this.serverPort = serverPort; 34 | mSocket = new DatagramSocket(port); 35 | mPacket = new DatagramPacket(message,message.length); 36 | thread = new HandlerThread("RTCPSocketThread"); 37 | thread.start(); 38 | isStoped = false; 39 | mHandler = new Handler(thread.getLooper()); 40 | } catch ( SocketException e ) { 41 | e.printStackTrace(); 42 | } 43 | } 44 | 45 | public void start() { 46 | mHandler.post(new Runnable() { 47 | @Override 48 | public void run() { 49 | while ( !isStoped ) { 50 | try { 51 | mSocket.receive(mPacket); 52 | } catch (IOException e) { 53 | Log.e(tag,e.toString()); 54 | } 55 | Log.e(tag,"Rtcp rev package " + mPacket.toString()); 56 | } 57 | } 58 | }); 59 | } 60 | 61 | public void stop(){ 62 | mSocket.close(); 63 | mSocket = null; 64 | mPacket = null; 65 | isStoped = true; 66 | thread.quit(); 67 | } 68 | 69 | public void sendReciverReport() { 70 | new Thread(new Runnable() { 71 | @Override 72 | public void run() { 73 | startSendReport(); 74 | } 75 | }).start(); 76 | } 77 | 78 | private void startSendReport() { 79 | byte[] sendData = new byte[2]; 80 | sendData[0] = (byte) Integer.parseInt("10000000",2); 81 | sendData[1] = (byte) 201; 82 | try { 83 | mPacket = new DatagramPacket(sendData,sendData.length, InetAddress.getByName(serverIp),serverPort); 84 | } catch (UnknownHostException e) { 85 | e.printStackTrace(); 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /rtspclient/RtspClinet/Socket/RtpSocket.java: -------------------------------------------------------------------------------- 1 | package com.aaronhan.rtspclient.RtspClinet.Socket; 2 | 3 | import android.database.sqlite.SQLiteCantOpenDatabaseException; 4 | import android.util.Log; 5 | 6 | import com.aaronhan.rtspclient.RtspClinet.Stream.RtpStream; 7 | 8 | import java.io.BufferedReader; 9 | import java.io.IOException; 10 | import java.io.InputStream; 11 | import java.io.InputStreamReader; 12 | import java.net.DatagramPacket; 13 | import java.net.DatagramSocket; 14 | import java.net.Socket; 15 | import java.util.concurrent.LinkedBlockingDeque; 16 | 17 | /** 18 | *RtpSocket is used to set up the rtp socket and receive the data via udp or tcp protocol 19 | * 1. set up the socket , four different socket : video udp socket, audio udp socket, video tcp socket, audio tcp socket 20 | * 2. make a thread to get the data form rtp server 21 | */ 22 | public class RtpSocket implements Runnable { 23 | private final static String tag = "RtpSocket"; 24 | private final static int TRACK_VIDEO = 0x01; 25 | private final static int TRACK_AUDIO = 0x02; 26 | 27 | private DatagramSocket mUdpSocket; 28 | private DatagramPacket mUdpPackets; 29 | private Socket mTcpSocket; 30 | private BufferedReader mTcpPackets; 31 | 32 | private RtcpSocket mRtcpSocket; 33 | private Thread mThread; 34 | private byte[] message = new byte[2048]; 35 | private int port; 36 | private String ip; 37 | private boolean isTcptranslate; 38 | private int trackType; 39 | private RtpStream mRtpStream; 40 | private int serverPort; 41 | private long recordTime = 0; 42 | private boolean useRtspTcpSocket,isStoped; 43 | private InputStream rtspInputStream; 44 | private LinkedBlockingDeque tcpBuffer = new LinkedBlockingDeque<>(); 45 | private Thread tcpThread; 46 | 47 | private static class rtspPacketInfo { 48 | public int len; 49 | public int offset; 50 | public boolean inNextPacket; 51 | public byte[] data; 52 | } 53 | private rtspPacketInfo rtspBuffer = new rtspPacketInfo(); 54 | private boolean packetFlag; 55 | 56 | public RtpSocket(boolean isTcptranslate, int port, String ip, int serverPort,int trackType) { 57 | this.port = port; 58 | this.ip = ip; 59 | this.trackType = trackType; 60 | this.isTcptranslate = isTcptranslate; 61 | this.serverPort = serverPort; 62 | this.isStoped = false; 63 | if(serverPort == -1) useRtspTcpSocket = false; 64 | else if(serverPort == -2) useRtspTcpSocket = true; 65 | try { 66 | if(!isTcptranslate) setupUdpSocket(); 67 | }catch ( IOException e ) { 68 | Log.e(tag, "Start the " + (isTcptranslate ? "tcp" : "udp") + "socket err!"); 69 | } 70 | } 71 | 72 | public void setRtspSocket(Socket s) { 73 | try { 74 | rtspInputStream = s.getInputStream(); 75 | } catch (IOException e) { 76 | e.printStackTrace(); 77 | } 78 | } 79 | 80 | private void setupUdpSocket() throws IOException { 81 | Log.d(tag,"Start to setup the udp socket , the port is: " + port + "...."); 82 | mUdpSocket = new DatagramSocket(port); 83 | mUdpPackets = new DatagramPacket(message,message.length); 84 | mRtcpSocket = new RtcpSocket(port+1,ip,serverPort+1); 85 | mRtcpSocket.start(); 86 | } 87 | 88 | private void tcpRecombineThread() { 89 | tcpThread = new Thread(new Runnable() { 90 | @Override 91 | public void run() { 92 | rtspBuffer.inNextPacket = false; 93 | int offset; 94 | while (!Thread.interrupted() && !isStoped) { 95 | try { 96 | byte[] tcpbuffer = tcpBuffer.take(); 97 | offset = 0; 98 | 99 | if(rtspBuffer.inNextPacket) { 100 | if(packetFlag) { 101 | rtspBuffer.len = ((tcpbuffer[0]&0xFF)<<8)|(tcpbuffer[1]&0xFF); 102 | rtspBuffer.data = new byte[rtspBuffer.len]; 103 | rtspBuffer.offset = 0; 104 | } 105 | 106 | if(rtspBuffer.len > tcpbuffer.length) { 107 | System.arraycopy(tcpbuffer,0,rtspBuffer.data,rtspBuffer.offset,tcpbuffer.length); 108 | rtspBuffer.offset += tcpbuffer.length; 109 | rtspBuffer.len = rtspBuffer.len - tcpbuffer.length; 110 | rtspBuffer.inNextPacket = true; 111 | } else { 112 | System.arraycopy(tcpbuffer, 0, rtspBuffer.data, rtspBuffer.offset, rtspBuffer.len); 113 | mRtpStream.receiveData(rtspBuffer.data, rtspBuffer.data.length); 114 | offset += rtspBuffer.len; 115 | rtspBuffer.inNextPacket = false; 116 | analysisOnePacket(tcpbuffer,offset); 117 | } 118 | }else{ 119 | analysisOnePacket(tcpbuffer,0); 120 | } 121 | } catch (InterruptedException e) { 122 | Log.e(tag,"The tcp buffer queue is empty.."); 123 | } 124 | } 125 | } 126 | },"TcpPacketRecombineThread"); 127 | tcpThread.start(); 128 | } 129 | 130 | private void analysisOnePacket(byte[] data, int offset){ 131 | int datalen; 132 | int[] tmp = getRtspFrameInfo(data,offset); 133 | byte[] buffer; 134 | datalen = data.length-tmp[0]; 135 | while(tmp[1] != -1) { 136 | if(tmp[1] == -2) { 137 | rtspBuffer.inNextPacket = true; 138 | packetFlag = true; 139 | break; 140 | } else packetFlag = false; 141 | if(tmp[1] < datalen) { 142 | //This packet have some rtsp frame 143 | buffer = new byte[tmp[1]]; 144 | System.arraycopy(data,tmp[0],buffer,0,tmp[1]); 145 | mRtpStream.receiveData(buffer,buffer.length); 146 | offset = tmp[0] + tmp[1]; 147 | tmp = getRtspFrameInfo(data,offset); 148 | datalen = data.length - tmp[0]; 149 | rtspBuffer.inNextPacket = false; 150 | } else if(tmp[1] > datalen) { 151 | //This packet have not enough rtsp frame, next packet have some 152 | rtspBuffer.data = new byte[tmp[1]]; 153 | rtspBuffer.len = tmp[1] - datalen; 154 | rtspBuffer.offset = datalen; 155 | if(rtspBuffer.offset != 0){ 156 | System.arraycopy(data,tmp[0],rtspBuffer.data,0,datalen); 157 | } 158 | rtspBuffer.inNextPacket = true; 159 | break; 160 | } else if(tmp[1] == datalen) { 161 | buffer = new byte[tmp[1]]; 162 | System.arraycopy(data,tmp[0], buffer, 0, tmp[1]); 163 | mRtpStream.receiveData(buffer,buffer.length); 164 | rtspBuffer.inNextPacket = false; 165 | break; 166 | } 167 | } 168 | } 169 | 170 | private int[] getRtspFrameInfo(byte[] data,int offset){ 171 | int mOffset,length; 172 | boolean haveRtspFrame = false; 173 | for(mOffset = offset; mOffset< data.length-1; ++mOffset){ 174 | if(data[mOffset] == 0x24 && data[mOffset+1] == 0x00) { 175 | haveRtspFrame = true; 176 | break; 177 | } 178 | haveRtspFrame = false; 179 | } 180 | if(haveRtspFrame) { 181 | if(mOffset + 3 < data.length) { 182 | length = ((data[mOffset + 2] & 0xFF) << 8) | (data[mOffset + 3] & 0xFF); 183 | mOffset += 4; 184 | } else length = -2; //This time 0x24 0x00 and data length is not in one packet 185 | } 186 | else 187 | length = -1; 188 | return new int[]{mOffset, length}; 189 | } 190 | 191 | private void useRtspTcpReading() throws IOException { 192 | int len; 193 | byte[] buffer = new byte[1024*10]; 194 | while((len = rtspInputStream.read(buffer)) != -1) { 195 | byte[] tcpbuffer = new byte[len]; 196 | System.arraycopy(buffer,0,tcpbuffer,0,len); 197 | try { 198 | tcpBuffer.put(tcpbuffer); 199 | } catch (InterruptedException e) { 200 | Log.e(tag,"The tcp queue buffer is full."); 201 | } 202 | } 203 | } 204 | 205 | private void setupTcpSocket() throws IOException { 206 | Log.d(tag, "Start to setup the tcp socket , the ip is: " + ip + ", the port is: " + port +"...."); 207 | mTcpSocket = new Socket(ip,port); 208 | mTcpPackets = new BufferedReader(new InputStreamReader(mTcpSocket.getInputStream())); 209 | Log.d(tag, "setup tcp socket done!"); 210 | } 211 | 212 | public void startRtpSocket() { 213 | Log.d(tag, "start to run rtp socket thread"); 214 | mThread = new Thread(this,"RTPSocketThread"); 215 | mThread.start(); 216 | } 217 | 218 | private void startTcpReading() throws IOException { 219 | String readLine; 220 | while ( (readLine = mTcpPackets.readLine()) != null ) { 221 | Log.d(tag, "the tcp read data is: " + readLine); 222 | } 223 | } 224 | 225 | private void startUdpReading() throws IOException { 226 | long currentTime; 227 | mUdpSocket.receive(mUdpPackets); 228 | byte[] buffer = new byte[mUdpPackets.getLength()]; 229 | System.arraycopy(mUdpPackets.getData(), 0, buffer, 0, mUdpPackets.getLength()); 230 | //Use Rtp stream thread to decode the receive data 231 | mRtpStream.receiveData(buffer, mUdpPackets.getLength()); 232 | 233 | //every 30s send a rtcp packet to server 234 | currentTime = System.currentTimeMillis(); 235 | if(currentTime-30000 > recordTime) { 236 | recordTime = currentTime; 237 | mRtcpSocket.sendReciverReport(); 238 | } 239 | } 240 | 241 | public void setStream(RtpStream stream) { 242 | mRtpStream = stream; 243 | } 244 | 245 | @Override 246 | public void run() { 247 | Log.d(tag, "start to get rtp data via socket..."); 248 | try { 249 | if(isTcptranslate) { 250 | tcpRecombineThread(); 251 | if(useRtspTcpSocket) { 252 | useRtspTcpReading(); 253 | } 254 | else{ 255 | setupTcpSocket(); 256 | startTcpReading(); 257 | } 258 | } else { 259 | while ( !Thread.interrupted() ) 260 | startUdpReading(); 261 | } 262 | } catch ( IOException e ) {} 263 | } 264 | 265 | public void stop() throws IOException { 266 | if(isTcptranslate) { 267 | if(mTcpSocket != null) { 268 | mTcpSocket.close(); 269 | mTcpPackets = null; 270 | } 271 | } 272 | else{ 273 | mUdpSocket.close(); 274 | mUdpPackets = null; 275 | } 276 | if(mRtcpSocket!=null){ 277 | mRtcpSocket.stop(); 278 | mRtcpSocket = null; 279 | } 280 | if(mThread!=null) mThread.interrupt(); 281 | isStoped = true; 282 | if(rtspBuffer!=null) rtspBuffer=null; 283 | tcpThread.interrupt(); 284 | } 285 | } 286 | -------------------------------------------------------------------------------- /rtspclient/RtspClinet/Stream/RtpStream.java: -------------------------------------------------------------------------------- 1 | package com.aaronhan.rtspclient.RtspClinet.Stream; 2 | 3 | import android.os.HandlerThread; 4 | import android.os.Handler; 5 | import android.util.Log; 6 | 7 | import java.util.concurrent.LinkedBlockingDeque; 8 | 9 | /** 10 | *This class is used to analysis the data from rtp socket , recombine it to video or audio stream 11 | * 1. get the data from rtp socket 12 | * 2. put the data into buffer 13 | * 3. use the thread to get the data from buffer, and unpack it 14 | */ 15 | public abstract class RtpStream { 16 | 17 | private final static String tag = "RtspStream"; 18 | protected final static int TRACK_VIDEO = 0x01; 19 | protected final static int TRACK_AUDIO = 0x02; 20 | 21 | private Handler mHandler; 22 | private byte[] buffer; 23 | private HandlerThread thread; 24 | private boolean isStoped; 25 | 26 | protected class StreamPacks { 27 | public boolean mark; 28 | public int pt; 29 | public long timestamp; 30 | public int sequenceNumber; 31 | public long Ssrc; 32 | public byte[] data; 33 | } 34 | 35 | private static class bufferUnit { 36 | public byte[] data; 37 | public int len; 38 | } 39 | 40 | private static LinkedBlockingDeque bufferQueue = new LinkedBlockingDeque<>(); 41 | 42 | public RtpStream() { 43 | thread = new HandlerThread("RTPStreamThread"); 44 | thread.start(); 45 | mHandler = new Handler(thread.getLooper()); 46 | unpackThread(); 47 | isStoped = false; 48 | } 49 | 50 | public static void receiveData(byte[] data, int len) { 51 | bufferUnit tmpBuffer = new bufferUnit(); 52 | tmpBuffer.data = new byte[len]; 53 | System.arraycopy(data,0,tmpBuffer.data,0,len); 54 | tmpBuffer.len = len; 55 | try { 56 | bufferQueue.put(tmpBuffer); 57 | } catch (InterruptedException e) { 58 | } 59 | } 60 | 61 | private void unpackThread() { 62 | mHandler.post(new Runnable() { 63 | @Override 64 | public void run() { 65 | bufferUnit tmpBuffer; 66 | while (!isStoped) { 67 | try { 68 | tmpBuffer = bufferQueue.take(); 69 | buffer = new byte[tmpBuffer.len]; 70 | System.arraycopy(tmpBuffer.data,0,buffer,0,tmpBuffer.len); 71 | unpackData(); 72 | } catch (InterruptedException e) { 73 | Log.e(tag,"wait the new data into the queue.."); 74 | } 75 | } 76 | buffer = null; 77 | bufferQueue.clear(); 78 | } 79 | }); 80 | } 81 | 82 | public void stop(){ 83 | isStoped = true; 84 | try { 85 | Thread.sleep(100); 86 | } catch (InterruptedException e) { 87 | e.printStackTrace(); 88 | } 89 | bufferQueue.clear(); 90 | buffer = null; 91 | thread.quit(); 92 | } 93 | 94 | 95 | protected abstract void recombinePacket(StreamPacks sp); 96 | 97 | private void unpackData() { 98 | StreamPacks tmpStreampack = new StreamPacks(); 99 | if(buffer.length == 0) return; 100 | int rtpVersion = (buffer[0]&0xFF)>>6; 101 | if(rtpVersion != 2) { 102 | Log.e(tag,"This is not a rtp packet."); 103 | return; 104 | } 105 | tmpStreampack.mark = (buffer[1] & 0xFF & 0x80) >> 7 == 1; 106 | tmpStreampack.pt = buffer[1] & 0x7F; 107 | tmpStreampack.sequenceNumber = ((buffer[2] & 0xFF) << 8) | (buffer[3] & 0xFF); 108 | tmpStreampack.timestamp = Long.parseLong(Integer.toHexString(((buffer[4] & 0xFF) << 24) | ((buffer[5]&0xFF) << 16) | ((buffer[6]&0xFF) << 8) | (buffer[7]&0xFF)) , 16); 109 | tmpStreampack.Ssrc = ((buffer[8]&0xFF) << 24) | ((buffer[9]&0xFF) << 16) | ((buffer[10]&0xFF) << 8) | (buffer[11]&0xFF); 110 | tmpStreampack.data = new byte[buffer.length-12]; 111 | 112 | System.arraycopy(buffer, 12, tmpStreampack.data, 0, buffer.length - 12); 113 | 114 | recombinePacket(tmpStreampack); 115 | } 116 | 117 | } 118 | -------------------------------------------------------------------------------- /rtspclient/RtspClinet/Video/H264Stream.java: -------------------------------------------------------------------------------- 1 | package com.aaronhan.rtspclient.RtspClinet.Video; 2 | 3 | import android.annotation.TargetApi; 4 | import android.media.MediaCodec; 5 | import android.media.MediaFormat; 6 | import android.os.Build; 7 | import android.os.Handler; 8 | import android.os.HandlerThread; 9 | import android.util.Base64; 10 | import android.util.Log; 11 | import android.view.SurfaceView; 12 | import android.view.View; 13 | import android.view.ViewStub; 14 | 15 | import com.aaronhan.rtspclient.RtspClient; 16 | 17 | import java.io.IOException; 18 | import java.nio.ByteBuffer; 19 | import java.util.concurrent.LinkedBlockingDeque; 20 | 21 | /** 22 | * 23 | */ 24 | public class H264Stream extends VideoStream { 25 | private final static String tag = "H24Stream"; 26 | 27 | private MediaCodec mMeidaCodec; 28 | private SurfaceView mSurfaceView; 29 | private ByteBuffer[] inputBuffers; 30 | private Handler mHandler; 31 | private LinkedBlockingDeque bufferQueue = new LinkedBlockingDeque<>(); 32 | private int picWidth,picHeight; 33 | byte[] header_sps,header_pps; 34 | private boolean isStop; 35 | private HandlerThread thread; 36 | 37 | @TargetApi(Build.VERSION_CODES.JELLY_BEAN) 38 | 39 | public H264Stream(RtspClient.SDPInfo sp) { 40 | mSDPinfo = sp; 41 | thread = new HandlerThread("H264StreamThread"); 42 | thread.start(); 43 | mHandler = new Handler(thread.getLooper()); 44 | if(sp.SPS != null) decodeSPS(); 45 | } 46 | 47 | private void configMediaDecoder(){ 48 | if(Build.VERSION.SDK_INT > 15) { 49 | try { 50 | mMeidaCodec = MediaCodec.createDecoderByType("Video/AVC"); 51 | MediaFormat mediaFormat = new MediaFormat(); 52 | mediaFormat.setString(MediaFormat.KEY_MIME, "Video/AVC"); 53 | mediaFormat.setByteBuffer("csd-0", ByteBuffer.wrap(header_sps)); 54 | mediaFormat.setByteBuffer("csd-1", ByteBuffer.wrap(header_pps)); 55 | mediaFormat.setInteger(MediaFormat.KEY_WIDTH, picWidth); 56 | mediaFormat.setInteger(MediaFormat.KEY_HEIGHT, picHeight); 57 | mMeidaCodec.configure(mediaFormat, mSurfaceView.getHolder().getSurface(), null, 0); 58 | mMeidaCodec.start(); 59 | inputBuffers = mMeidaCodec.getInputBuffers(); 60 | isStop = false; 61 | } catch (IOException e) { 62 | e.printStackTrace(); 63 | } 64 | } 65 | } 66 | 67 | 68 | public void startMediaHardwareDecode() { 69 | mHandler.post(hardwareDecodeThread); 70 | } 71 | 72 | private Runnable hardwareDecodeThread = new Runnable() { 73 | @Override 74 | public void run() { 75 | int mCount = 0; 76 | int inputBufferIndex,outputBufferIndex; 77 | MediaCodec.BufferInfo info = new MediaCodec.BufferInfo(); 78 | byte[] tmpByte; 79 | int framType; 80 | boolean startKeyFrame = false; 81 | 82 | configMediaDecoder(); 83 | while (!Thread.interrupted() && !isStop) { 84 | try { 85 | tmpByte = bufferQueue.take(); 86 | framType = tmpByte[4]&0x1F; 87 | if(framType == 5) startKeyFrame = true; 88 | if(startKeyFrame || framType == 7 || framType == 8) { 89 | inputBufferIndex = mMeidaCodec.dequeueInputBuffer(2000); 90 | if (inputBufferIndex > 1) { 91 | ByteBuffer inputBuffer = inputBuffers[inputBufferIndex]; 92 | inputBuffer.clear(); 93 | inputBuffer.put(tmpByte); 94 | mMeidaCodec.queueInputBuffer(inputBufferIndex, 0, tmpByte.length, mCount, 0); 95 | outputBufferIndex = mMeidaCodec.dequeueOutputBuffer(info, 1000); 96 | switch (outputBufferIndex) { 97 | case MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED: 98 | break; 99 | case MediaCodec.INFO_OUTPUT_FORMAT_CHANGED: 100 | break; 101 | case MediaCodec.INFO_TRY_AGAIN_LATER: 102 | break; 103 | default: 104 | mMeidaCodec.releaseOutputBuffer(outputBufferIndex, true); 105 | break; 106 | } 107 | mCount++; 108 | } 109 | } 110 | } catch (InterruptedException e) { 111 | Log.e(tag,"Wait the buffer come.."); 112 | } 113 | } 114 | bufferQueue.clear(); 115 | mMeidaCodec.stop(); 116 | mMeidaCodec.release(); 117 | mMeidaCodec = null; 118 | } 119 | }; 120 | 121 | public void stop(){ 122 | bufferQueue.clear(); 123 | isStop = true; 124 | try { 125 | Thread.sleep(100); 126 | } catch (InterruptedException e) {} 127 | mHandler.removeCallbacks(hardwareDecodeThread); 128 | if(mMeidaCodec != null) { 129 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { 130 | mMeidaCodec.stop(); 131 | mMeidaCodec.release(); 132 | } 133 | mMeidaCodec = null; 134 | } 135 | super.stop(); 136 | thread.quit(); 137 | mSurfaceView.setVisibility(ViewStub.GONE); 138 | mSurfaceView.setVisibility(ViewStub.VISIBLE); 139 | } 140 | 141 | /* This method is used to decode pic width and height from the sps info, 142 | * which got from the RTSP DESCRIPE request, SDP info. 143 | */ 144 | private void decodeSPS(){ 145 | int i,offset = 32; 146 | int pic_width_len,pic_height_len; 147 | byte[] sps = Base64.decode(mSDPinfo.SPS, Base64.NO_WRAP); 148 | byte[] pps = Base64.decode(mSDPinfo.PPS, Base64.NO_WRAP); 149 | int profile_idc = sps[1]; 150 | header_pps = new byte[pps.length]; 151 | header_sps = new byte[sps.length]; 152 | System.arraycopy(sps,0,header_sps,0,sps.length); 153 | System.arraycopy(pps,0,header_pps,0,pps.length); 154 | offset += getUeLen(sps,offset);//jump seq_parameter_set_id 155 | if(profile_idc == 100 || profile_idc == 110 || profile_idc == 122 156 | || profile_idc == 144) { 157 | int chroma_format_idc = (getUeLen(sps,offset) == 1)?0: 158 | ( sps[(offset+getUeLen(sps,offset))/8] >> 159 | (7-((offset+getUeLen(sps,offset))%8)) ); 160 | offset += getUeLen(sps,offset);//jump chroma_format_idc 161 | if(chroma_format_idc == 3) 162 | offset++; //jump residual_colour_transform_flag 163 | offset += getUeLen(sps,offset);//jump bit_depth_luma_minus8 164 | offset += getUeLen(sps,offset);//jump bit_depth_chroma_minus8 165 | offset ++; //jump qpprime_y_zero_transform_bypass_flag 166 | int seq_scaling_matrix_present_flag = (sps[offset/8] >> (8-(offset%8)))&0x01; 167 | if(seq_scaling_matrix_present_flag == 1) offset += 8; //jump seq_scaling_list_present_flag 168 | } 169 | offset += getUeLen(sps,offset);//jump log2_max_frame_num_minus4 170 | int pic_order_cnt_type = (getUeLen(sps,offset) == 1)?0: 171 | ( sps[(offset+getUeLen(sps,offset))/8] >> 172 | (7-((offset+getUeLen(sps,offset))%8)) ); 173 | offset += getUeLen(sps,offset); 174 | if(pic_order_cnt_type == 0) { 175 | offset += getUeLen(sps,offset); 176 | } 177 | else if(pic_order_cnt_type == 1) { 178 | offset++; //jump delta_pic_order_always_zero_flag 179 | offset += getUeLen(sps,offset); //jump offset_for_non_ref_pic 180 | offset += getUeLen(sps,offset); //jump offset_for_top_to_bottom_field 181 | int num_ref_frames_inpic_order_cnt_cycle = ( sps[(offset+getUeLen(sps,offset))/8] >> 182 | (7-((offset+getUeLen(sps,offset))%8)) ); 183 | for(i=0; i