点击打开链接使用Android Media Codec播放RTSP视频流
优化点:
1、收包和组包在一个线程里面,没有分开,如有需要做包序整理或者其他修改,可以考虑将收包和组包放在两个并行的线程;
2、停止播放,没有做线程同步,反复快速的停止/播放切换,可能会导致创建多个重复线程;建议停止时,对所有线程执行join(),对sleep的线程执行interrupt(),join();
3、没有监听mediacodec解码出错的情况,建议添加监听,然后reset()mediacodec。这样可以无缝重启播放;
4、目前有测试到一款手机无法硬解码,报错,调试后发现是,必须要在硬解码器初始化的时候设置sps和pps,以下注释需要打开。
// byte[] header_sps = new byte[]{0, 0, 0, 1, 103, 66, 0, 42, -106, 53, 64, -16, 4, 79, -53, 55};
// byte[] header_pps = {0, 0, 0, 1, 104, -50, 60, -128};
// mediaFormat.setByteBuffer("csd-0", ByteBuffer.wrap(header_sps));
// mediaFormat.setByteBuffer("csd-1", ByteBuffer.wrap(header_pps));
其他问题欢迎留言。
简单说就3步(完整AS工程源代码在文章末尾有下载,下载后,请用本文中的GeneROVPlay.java代码替换下载中的相应文件,具有更好的兼容性):
1、创建一个byte[]的list来接收rtsp的数据。
private BlockingQueue video_data_Queue = new ArrayBlockingQueue(1000);
2、建立一个线程不停的接受rtsp数据包,组合好每一个完整帧存到创建好的byte[]list里面去。
在这里你可能需要了解下RTSP具体是按照什么格式发送H264视频流的,这个csdn上面有很多blog讲这个,按照那个发送格式解析就好。这里引用一个讲的比较好的介绍:https://blog.csdn.net/chen495810242/article/details/39207305
注意,RTSP命令协议中包含GET_PARAMETER命令,此命令用来告诉rtsp服务器,客户单是否还保持在线,之前demo代码中没有添加这个命令,可能会导致某些rtsp器主动断掉视频流,以下代码中已经添加一个新的线程,每隔3s调用sendParam()发送一次,具体时间可以看各自的rtsp服务器设置而定。
public void run() {
DatagramSocket dataSocket=null;
DatagramPacket dataPacket;
Socket socket;
OutputStream outputStream;
byte frame_head_1 = (byte)0x00;
byte frame_head_2 = (byte)0x00;
byte frame_head_3 = (byte)0x00;
byte frame_head_4 = (byte)0x01;
byte frame_head_I = (byte)0x65;
byte frame_head_P = (byte)0x61;
int nal_unit_type;
long lastSq = 0;
long currSq = 0;
Log.d(TAG,"MediaCodecThread running.");
try {
socket = new Socket();
SocketAddress socketAddress = new InetSocketAddress(playUrlIp, playUrlPort);
socket.connect(socketAddress, 3000);
reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
outputStream = socket.getOutputStream();
writer = new BufferedWriter(new OutputStreamWriter(outputStream));
writer.write(sendOptions());
writer.flush();
getResponse();
writer.write(sendDescribe());
writer.flush();
getResponse();
writer.write(sendSetup());
writer.flush();
getResponse();
writer.write(sendPlay());
writer.flush();
getResponse();
new Thread(new Runnable() {
@Override
public void run() {
while(isPlaying){
try {
writer.write(sendParam());
writer.flush();
Thread.sleep(3*1000);
} catch (InterruptedException | IOException e) {
e.printStackTrace();
if(e instanceof IOException){
isPlaying = false;
videoPort = videoPort + 2;
audioPort = audioPort + 2;
}
}
};
}
}).start();
Log.d(TAG,"start DatagramSocket.");
dataSocket = new DatagramSocket(videoPort);
dataSocket.setSoTimeout(3000);
byte[] receiveByte = new byte[48*1024];//96
//从udp读取的数据长度
int offHeadsize = 0;
//当前帧长度
int frameLen = 0;
//完整帧筛选用缓冲区
byte[] frame = new byte[FRAME_MAX_LEN];
dataPacket = new DatagramPacket(receiveByte, receiveByte.length);
Log.d(TAG,"start receive data from socket.");
while(isPlaying){
//Log.d(TAG, "T=" + test);
dataSocket.receive(dataPacket);
offHeadsize = dataPacket.getLength() - 12;
if (offHeadsize > 2) {
lastSq = currSq;
currSq = ((receiveByte[2] & 0xFF)<<8) + (receiveByte[3] & 0xFF);
//Log.d(".", "~~" + currSq);
if(lastSq != 0){
if(lastSq != currSq - 1){
Log.d(TAG, "frame data maybe lost.last=" + lastSq + ",curr=" + currSq);
}
}
if (frameLen + offHeadsize < FRAME_MAX_LEN) {
nal_unit_type = receiveByte[12]&0xFF;
if(nal_unit_type == 0x67 /*SPS*/
|| nal_unit_type == 0x68 /*PPS*/
|| nal_unit_type == 0x6 /*SEI*/){
//加上头部
receiveByte[8] = frame_head_1;
receiveByte[9] = frame_head_2;
receiveByte[10] = frame_head_3;
receiveByte[11] = frame_head_4;
//Log.d(TAG, "ppp=" + Arrays.toString(receiveByte));
video_data_Queue.put(Arrays.copyOfRange(receiveByte, 8, offHeadsize + 12));
// if(isFirstPacket){
// header_sps = Arrays.copyOfRange(receiveByte, 8, offHeadsize + 12);
// mediaCodec = null;
// initMediaCodec();
// startDecodecThread();
// isFirstPacket = false;
// }
//修改frameLen
frameLen = 0;
}else if((nal_unit_type&0x1F) == 28){//分片NAL包,可能是I或者P帧
if((receiveByte[13]&0xFF) == 0x85){
//I帧的第一包
// Log.e(TAG, "I1=" + System.currentTimeMillis());
receiveByte[9] = frame_head_1;
receiveByte[10] = frame_head_2;
receiveByte[11] = frame_head_3;
receiveByte[12] = frame_head_4;
receiveByte[13] = frame_head_I;
System.arraycopy(receiveByte, 9, frame, frameLen, offHeadsize + 3);
frameLen += offHeadsize + 3;
}else if((receiveByte[13]&0xFF) == 0x81){
//P帧的第一包
// Log.e(TAG, "P1=" + System.currentTimeMillis());
receiveByte[9] = frame_head_1;
receiveByte[10] = frame_head_2;
receiveByte[11] = frame_head_3;
receiveByte[12] = frame_head_4;
receiveByte[13] = frame_head_P;
System.arraycopy(receiveByte, 9, frame, frameLen, offHeadsize + 3);
frameLen += offHeadsize + 3;
}else{
System.arraycopy(receiveByte, 14, frame, frameLen, offHeadsize - 2);
//修改frameLen
frameLen += offHeadsize - 2;
}
if(((receiveByte[13]&0xFF) == 0x45)){
// Log.e(TAG, "II1=" + System.currentTimeMillis());
video_data_Queue.put(Arrays.copyOfRange(frame, 0, frameLen));
frameLen = 0;
}else if(((receiveByte[13]&0xFF) == 0x41)){
// Log.e(TAG, "PP2=" + System.currentTimeMillis());
video_data_Queue.put(Arrays.copyOfRange(frame, 0, frameLen));
frameLen = 0;
}
}
//Log.d(TAG, "SQ:" + (((receiveByte[2] & 0xFF)<<8) + (receiveByte[3] & 0xFF)));
//Log.d(TAG, "udp data=" + Arrays.toString(dataPacket.getData()));
//Log.d(TAG, "data=" + Arrays.toString(receiveByte));
// Log.d(TAG, "-------");
// Log.d(TAG, "rtp V:" + ((receiveByte[0] & 0xC0)>>6));
// Log.d(TAG, "rtp P:" + ((receiveByte[0] & 0x20)>>5));
// Log.d(TAG, "rtp X:" + ((receiveByte[0] & 0x10)>>4));
// Log.d(TAG, "rtp M:" + ((receiveByte[1] & 0x80)>>7));
// Log.d(TAG, "rtp PT:" + (receiveByte[1] & 0x7F));
// Log.d(TAG, "rtp SQ:" + (((receiveByte[2] & 0xFF)<<8) + (receiveByte[3] & 0xFF)));
// Log.d(TAG, "rtp TS:" + (((receiveByte[7] & 0xFF)<<24) + ((receiveByte[6] & 0xFF)<<16) + ((receiveByte[5] & 0xFF)<<8) + (receiveByte[4] & 0xFF)));
// Log.d(TAG, "-------");
}
}else{
isPlaying = false;
Log.e(TAG, "udp port receive stream failed.");
}
}
} catch (Exception e) {
e.printStackTrace();
isPlaying = false;
Log.d(TAG,"receive data from socket failed.");
}finally {
video_data_Queue.clear();
if (dataSocket != null) {
try {
writer.write(sendTearDown());
writer.flush();
dataSocket.close();
Log.e(TAG, "dataSocket close ok.");
} catch (Exception e) {
//dataSocket.close();
Log.e(TAG, "dataSocket close failed.",e);
}
}
}
}
3、建立一个线程来从byte[]list里面读取视频数据,喂给解码器,同时不停的获取解码器的输出,渲染到surface。
注意:demo中代码的解码线程中没有增加sleep休眠会导致CPU占用过高,可以参考下面的方法在帧与帧之间增加sleep休眠,降低CPU占用率,我测试以20ms间隔,cpu占用率在5%左右的水平。
public void run() {
while(isPlaying){
int inIndex = -1;
try {
inIndex = mediaCodec.dequeueInputBuffer(5);
} catch (Exception e) {
return;
}
try {
if (inIndex >= 0) {
ByteBuffer buffer = inputBuffers[inIndex];
buffer.clear();
if (!video_data_Queue.isEmpty()) {
byte[] data;
data = video_data_Queue.take();
buffer.put(data);
mediaCodec.queueInputBuffer(inIndex, 0, data.length, 66, 0);
//Log.d(TAG, "F=" + System.currentTimeMillis());
} else {
mediaCodec.queueInputBuffer(inIndex, 0, 0, 66, 0);
}
} /*else {
mediaCodec.queueInputBuffer(inIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
}*/
int outIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, 0);
if(outIndex > 0){
mediaCodec.releaseOutputBuffer(outIndex, true);
lasttime = System.currentTimeMillis();
}
} catch (Exception e) {
Log.e(TAG, "startDecodecThread have fatal error.");
e.printStackTrace();
}
try {
if(lasttime != 0){
frametime = System.currentTimeMillis() - lasttime;
if(frametime < 20){
Thread.sleep(20 - frametime);
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
isPlaying = false;
mediaCodec.stop();
mediaCodec.release();
mediaCodec = null;
}
话不多说,上代码。
史上最简单的demo,稳定测试华为平板,三星手机,小米平板2,3,摩托罗拉手机都完美播放。demo是最精简,延时最低的一个客户端,不包含音频部分。减少了buff的复制次数,为了就是实现最低延时。
如果某些rtsp的视频流无法播放,可能是rtsp命令协议:sendOptions(),sendDescribe(),sendSetup(),sendPlay()函数需要根据具体的rtsp客户端来修改下。绝大部分rtsp应该都是没有问题的。
完整代码如下:因为demo资源上传了无法修改,可以直接用以下代码替换下载demo代码中的GeneROVPlayer.java,然后直接build即可
package com.gene.fanxplayerdemo;
import android.media.MediaCodec;
import android.media.MediaFormat;
import android.util.Log;
import android.view.Surface;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
/**
* Created by Auser on 2018/5/28.
*/
public class GeneROVPlayer {
private final static String TAG = "GeneROVPlayer";
private final static String MIME_TYPE = "video/avc"; // H.264 Advanced Video
private BlockingQueue video_data_Queue = new ArrayBlockingQueue(1000);
private ByteBuffer[] inputBuffers;
private MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
//MediaCodec variable
private boolean isPlaying = false;
private Surface surface;
private MediaCodec mediaCodec;
private long lasttime = 0;
private long frametime = 0;
// private byte[] header_sps;
//rtsp variable
private String playUrl;
private String playUrlIp;
private int playUrlPort;
private int mCSeq = 0;
private String sessionId;
private int videoPort = 50001;
private int audioPort = 50002;
private final int FRAME_MAX_LEN = 300 * 1024;
// private boolean isFirstPacket = true;
private BufferedReader reader;
private BufferedWriter writer;
//for test variable
private int test = 0;
public GeneROVPlayer(Surface surface){
this.surface = surface;
initMediaCodec();
};
/*
设置视频流
*/
public void setPlayUrl(String playUrl){
//rtsp://192.168.8.8:8554/stream
String str[] = playUrl.split("//");
if(str.length == 2 && str[0].equals("rtsp:")){
if(str[1].contains(":")){
this.playUrl = playUrl;
str = str[1].split(":");
playUrlIp = str[0];
playUrlPort = Integer.parseInt(str[1].split("/")[0]);
}
}else{
Log.e(TAG, "setPlayUrl failed,playUrl illegality.");
}
}
/*
开始播放
*/
public void startPlay(){
if(mediaCodec == null){
initMediaCodec();
}
if(isPlaying){
Log.e(TAG, "start play failed.player is playing.");
}else{
isPlaying = true;
startDecodecThread();
startRtspThread();
}
}
/*
停止播放
*/
public void stopPlay(){
isPlaying = false;
}
/*
初始化MediaCodec
*/
private void initMediaCodec(){
try {
MediaFormat mediaFormat = MediaFormat.createVideoFormat(MIME_TYPE,1920, 1088);
// byte[] header_sps = new byte[]{0, 0, 0, 1, 103, 66, 0, 42, -106, 53, 64, -16, 4, 79, -53, 55};
// byte[] header_pps = {0, 0, 0, 1, 104, -50, 60, -128};
// byte[] header_sps = new byte[]{0, 0, 0, 1, 103, 66, 0, 31, -106, 53, 64, -96, 11, 116, -36, 4, 4, 4, 8};
// byte[] header_pps = {0, 0, 0, 1, 104, -50, 60, -128};
// mediaFormat.setByteBuffer("csd-0", ByteBuffer.wrap(header_sps));
// mediaFormat.setByteBuffer("csd-1", ByteBuffer.wrap(header_pps));
mediaCodec = MediaCodec.createDecoderByType(MIME_TYPE);
mediaCodec.configure(mediaFormat, surface, null, 0);
mediaCodec.start();
inputBuffers = mediaCodec.getInputBuffers();
} catch (IOException e) {
e.printStackTrace();
}
}
/*
开启解码线程
*/
private void startDecodecThread(){
new Thread(new Runnable() {
@Override
public void run() {
while(isPlaying){
int inIndex = -1;
try {
inIndex = mediaCodec.dequeueInputBuffer(5);
} catch (Exception e) {
return;
}
try {
if (inIndex >= 0) {
ByteBuffer buffer = inputBuffers[inIndex];
buffer.clear();
if (!video_data_Queue.isEmpty()) {
byte[] data;
data = video_data_Queue.take();
buffer.put(data);
mediaCodec.queueInputBuffer(inIndex, 0, data.length, 66, 0);
//Log.d(TAG, "F=" + System.currentTimeMillis());
} else {
mediaCodec.queueInputBuffer(inIndex, 0, 0, 66, 0);
}
} /*else {
mediaCodec.queueInputBuffer(inIndex, 0, 0, 0, MediaCodec.BUFFER_FLAG_END_OF_STREAM);
}*/
int outIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, 0);
if(outIndex > 0){
mediaCodec.releaseOutputBuffer(outIndex, true);
lasttime = System.currentTimeMillis();
}
} catch (Exception e) {
Log.e(TAG, "startDecodecThread have fatal error.");
e.printStackTrace();
}
try {
if(lasttime != 0){
frametime = System.currentTimeMillis() - lasttime;
if(frametime < 20){
Thread.sleep(20 - frametime);
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
isPlaying = false;
mediaCodec.stop();
mediaCodec.release();
mediaCodec = null;
}
}).start();
}
/*
开启RTSP收包线程
*/
private void startRtspThread(){
new Thread(new Runnable() {
@Override
public void run() {
DatagramSocket dataSocket=null;
DatagramPacket dataPacket;
Socket socket;
OutputStream outputStream;
byte frame_head_1 = (byte)0x00;
byte frame_head_2 = (byte)0x00;
byte frame_head_3 = (byte)0x00;
byte frame_head_4 = (byte)0x01;
byte frame_head_I = (byte)0x65;
byte frame_head_P = (byte)0x61;
int nal_unit_type;
long lastSq = 0;
long currSq = 0;
Log.d(TAG,"MediaCodecThread running.");
try {
socket = new Socket();
SocketAddress socketAddress = new InetSocketAddress(playUrlIp, playUrlPort);
socket.connect(socketAddress, 3000);
reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
outputStream = socket.getOutputStream();
writer = new BufferedWriter(new OutputStreamWriter(outputStream));
writer.write(sendOptions());
writer.flush();
getResponse();
writer.write(sendDescribe());
writer.flush();
getResponse();
writer.write(sendSetup());
writer.flush();
getResponse();
writer.write(sendPlay());
writer.flush();
getResponse();
new Thread(new Runnable() {
@Override
public void run() {
while(isPlaying){
try {
writer.write(sendParam());
writer.flush();
Thread.sleep(3*1000);
} catch (InterruptedException | IOException e) {
e.printStackTrace();
if(e instanceof IOException){
isPlaying = false;
videoPort = videoPort + 2;
audioPort = audioPort + 2;
}
}
};
}
}).start();
Log.d(TAG,"start DatagramSocket.");
dataSocket = new DatagramSocket(videoPort);
dataSocket.setSoTimeout(3000);
byte[] receiveByte = new byte[48*1024];//96
//从udp读取的数据长度
int offHeadsize = 0;
//当前帧长度
int frameLen = 0;
//完整帧筛选用缓冲区
byte[] frame = new byte[FRAME_MAX_LEN];
dataPacket = new DatagramPacket(receiveByte, receiveByte.length);
Log.d(TAG,"start receive data from socket.");
while(isPlaying){
//Log.d(TAG, "T=" + test);
dataSocket.receive(dataPacket);
offHeadsize = dataPacket.getLength() - 12;
if (offHeadsize > 2) {
lastSq = currSq;
currSq = ((receiveByte[2] & 0xFF)<<8) + (receiveByte[3] & 0xFF);
//Log.d(".", "~~" + currSq);
if(lastSq != 0){
if(lastSq != currSq - 1){
Log.d(TAG, "frame data maybe lost.last=" + lastSq + ",curr=" + currSq);
}
}
if (frameLen + offHeadsize < FRAME_MAX_LEN) {
nal_unit_type = receiveByte[12]&0xFF;
if(nal_unit_type == 0x67 /*SPS*/
|| nal_unit_type == 0x68 /*PPS*/
|| nal_unit_type == 0x6 /*SEI*/){
//加上头部
receiveByte[8] = frame_head_1;
receiveByte[9] = frame_head_2;
receiveByte[10] = frame_head_3;
receiveByte[11] = frame_head_4;
//Log.d(TAG, "ppp=" + Arrays.toString(receiveByte));
video_data_Queue.put(Arrays.copyOfRange(receiveByte, 8, offHeadsize + 12));
// if(isFirstPacket){
// header_sps = Arrays.copyOfRange(receiveByte, 8, offHeadsize + 12);
// mediaCodec = null;
// initMediaCodec();
// startDecodecThread();
// isFirstPacket = false;
// }
//修改frameLen
frameLen = 0;
}else if((nal_unit_type&0x1F) == 28){//分片NAL包,可能是I或者P帧
if((receiveByte[13]&0xFF) == 0x85){
//I帧的第一包
// Log.e(TAG, "I1=" + System.currentTimeMillis());
receiveByte[9] = frame_head_1;
receiveByte[10] = frame_head_2;
receiveByte[11] = frame_head_3;
receiveByte[12] = frame_head_4;
receiveByte[13] = frame_head_I;
System.arraycopy(receiveByte, 9, frame, frameLen, offHeadsize + 3);
frameLen += offHeadsize + 3;
}else if((receiveByte[13]&0xFF) == 0x81){
//P帧的第一包
// Log.e(TAG, "P1=" + System.currentTimeMillis());
receiveByte[9] = frame_head_1;
receiveByte[10] = frame_head_2;
receiveByte[11] = frame_head_3;
receiveByte[12] = frame_head_4;
receiveByte[13] = frame_head_P;
System.arraycopy(receiveByte, 9, frame, frameLen, offHeadsize + 3);
frameLen += offHeadsize + 3;
}else{
System.arraycopy(receiveByte, 14, frame, frameLen, offHeadsize - 2);
//修改frameLen
frameLen += offHeadsize - 2;
}
if(((receiveByte[13]&0xFF) == 0x45)){
// Log.e(TAG, "II1=" + System.currentTimeMillis());
video_data_Queue.put(Arrays.copyOfRange(frame, 0, frameLen));
frameLen = 0;
}else if(((receiveByte[13]&0xFF) == 0x41)){
// Log.e(TAG, "PP2=" + System.currentTimeMillis());
video_data_Queue.put(Arrays.copyOfRange(frame, 0, frameLen));
frameLen = 0;
}
}
//Log.d(TAG, "SQ:" + (((receiveByte[2] & 0xFF)<<8) + (receiveByte[3] & 0xFF)));
//Log.d(TAG, "udp data=" + Arrays.toString(dataPacket.getData()));
//Log.d(TAG, "data=" + Arrays.toString(receiveByte));
// Log.d(TAG, "-------");
// Log.d(TAG, "rtp V:" + ((receiveByte[0] & 0xC0)>>6));
// Log.d(TAG, "rtp P:" + ((receiveByte[0] & 0x20)>>5));
// Log.d(TAG, "rtp X:" + ((receiveByte[0] & 0x10)>>4));
// Log.d(TAG, "rtp M:" + ((receiveByte[1] & 0x80)>>7));
// Log.d(TAG, "rtp PT:" + (receiveByte[1] & 0x7F));
// Log.d(TAG, "rtp SQ:" + (((receiveByte[2] & 0xFF)<<8) + (receiveByte[3] & 0xFF)));
// Log.d(TAG, "rtp TS:" + (((receiveByte[7] & 0xFF)<<24) + ((receiveByte[6] & 0xFF)<<16) + ((receiveByte[5] & 0xFF)<<8) + (receiveByte[4] & 0xFF)));
// Log.d(TAG, "-------");
}
}else{
isPlaying = false;
Log.e(TAG, "udp port receive stream failed.");
}
}
} catch (Exception e) {
e.printStackTrace();
isPlaying = false;
Log.d(TAG,"receive data from socket failed.");
}finally {
video_data_Queue.clear();
if (dataSocket != null) {
try {
writer.write(sendTearDown());
writer.flush();
dataSocket.close();
Log.e(TAG, "dataSocket close ok.");
} catch (Exception e) {
//dataSocket.close();
Log.e(TAG, "dataSocket close failed.",e);
}
}
}
}
}).start();
}
/*
rtsp协议:OPTIONS
}
*/
private String sendOptions() {
String options =
"OPTIONS " + playUrl + " RTSP/1.0\r\n" + addHeaders();
Log.i(TAG, options);
return options;
}
/*
rtsp协议:DESCRIBE
*/
private String sendDescribe() {
String describe =
"DESCRIBE " + playUrl + " RTSP/1.0\r\n" + addHeaders();
Log.i(TAG, describe);
return describe;
}
/*
rtsp协议:SETUP
*/
private String sendSetup() {
String setup =
"SETUP " + playUrl + "/track0" + " RTSP/1.0\r\n"
+ "Transport: RTP/AVP;unicast;client_port=" + videoPort + "-" + audioPort + "\r\n"
+ addHeaders();
Log.i(TAG, setup);
return setup;
}
/*
rtsp协议:PLAY
*/
private String sendPlay(){
String play =
"PLAY " + playUrl + " RTSP/1.0\r\n"
+ (sessionId != null ? "Session: " + sessionId + "\r\n":"")
+ addHeaders();
Log.i(TAG, play);
return play;
}
/*
rtst协议:GET_PARAMETER
*/
private String sendParam(){
String param =
"GET_PARAMETER " + playUrl + " RTSP/1.0\r\n"
+ (sessionId != null ? "Session: " + sessionId + "\r\n":"")
+ addHeaders();
Log.i(TAG, param);
return param;
}
/*
rtsp协议:TEARDOWN
*/
private String sendTearDown() {
String teardown =
"TEARDOWN " + playUrl + " RTSP/1.0\r\n"
+ (sessionId != null ? "Session: " + sessionId + "\r\n":"")
+ addHeaders();
Log.i(TAG, teardown);
return teardown;
}
private String addHeaders() {
return "CSeq: "
+ (++mCSeq)
+ "\r\n"
+ "User-Agent: GeneROV/Android MediaCodec\r\n"
+ "\r\n";
}
/*
rtsp协议:解析返回
*/
private void getResponse(){
try {
String line;
int cnt = 0;
while ((line = reader.readLine()) != null) {
Log.d(TAG, line);
if(line.contains("Session:")){
sessionId = line.split(";")[0].split(":")[1].trim();
}
if(line.contains("sdp")){
cnt = 2;//10;
}
cnt--;
if( line.length() < 2 && cnt <= 0)break;
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
demo:https://download.csdn.net/download/fanx9339/10493707