使用Android Media Codec 播放rtsp视频流

点击打开链接使用Android Media Codec播放RTSP视频流

demo只能播放H264的rtsp视频流,只是演示H264组包和硬解码,代码线程和结构并未优化,实际项目应用建议重点了解,缓冲和延时的动态平衡,丢帧策略等。

优化点:

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<byte[]> video_data_Queue = new ArrayBlockingQueue<byte[]>(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<byte[]> video_data_Queue = new ArrayBlockingQueue<byte[]>(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    

评论 19
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

静默安装9339

希望本文章对你有一点点帮助

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值