├── .gitignore ├── .idea ├── gradle.xml ├── misc.xml ├── modules.xml ├── runConfigurations.xml └── vcs.xml ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── rockchip │ │ └── rkmediacodecdemo │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── rockchip │ │ │ └── rkmediacodecdemo │ │ │ ├── MainActivity.java │ │ │ ├── PlayerView.java │ │ │ ├── PortScanner.java │ │ │ ├── RKMediaplayer.java │ │ │ └── rtsp │ │ │ ├── AACPackage.java │ │ │ ├── H264Package.java │ │ │ ├── NALUnit.java │ │ │ ├── RTCPPackage.java │ │ │ ├── RTPConnector.java │ │ │ ├── RTPPackage.java │ │ │ └── RTSPConnector.java │ └── res │ │ ├── drawable-v24 │ │ └── ic_launcher_foreground.xml │ │ ├── drawable │ │ └── ic_launcher_background.xml │ │ ├── layout │ │ └── activity_main.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ └── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── rockchip │ └── rkmediacodecdemo │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── rkmediacodec ├── .gitignore ├── CMakeLists.txt ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── rockchip │ │ └── rkmediacodec │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── cpp │ │ ├── log.h │ │ ├── native-lib.cpp │ │ ├── rk_mpp.cpp │ │ └── rk_mpp.h │ ├── java │ │ └── com │ │ │ └── rockchip │ │ │ └── rkmediacodec │ │ │ └── RKMediaCodec.java │ ├── jniLibs │ │ ├── arm64-v8a │ │ │ ├── libmpp.so │ │ │ └── libvpu.so │ │ └── include │ │ │ ├── mpp_buffer.h │ │ │ ├── mpp_err.h │ │ │ ├── mpp_frame.h │ │ │ ├── mpp_meta.h │ │ │ ├── mpp_packet.h │ │ │ ├── mpp_task.h │ │ │ ├── rk_mpi.h │ │ │ ├── rk_mpi_cmd.h │ │ │ ├── rk_type.h │ │ │ ├── vpu.h │ │ │ └── vpu_api.h │ └── res │ │ └── values │ │ └── strings.xml │ └── test │ └── java │ └── com │ └── rockchip │ └── rkmediacodec │ └── ExampleUnitTest.java └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | .externalNativeBuild 10 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 18 | 19 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 16 | 26 | 27 | 28 | 29 | 30 | 31 | 33 | 34 | 35 | 36 | 37 | 1.8 38 | 39 | 44 | 45 | 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 26 5 | defaultConfig { 6 | applicationId "com.rockchip.rkmediacodecdemo" 7 | minSdkVersion 25 8 | targetSdkVersion 26 9 | versionCode 1 10 | versionName "1.0" 11 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 12 | } 13 | buildTypes { 14 | release { 15 | minifyEnabled false 16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 17 | } 18 | } 19 | } 20 | 21 | dependencies { 22 | implementation fileTree(include: ['*.jar'], dir: 'libs') 23 | implementation 'com.android.support:appcompat-v7:26.1.0' 24 | implementation 'com.android.support.constraint:constraint-layout:1.0.2' 25 | testImplementation 'junit:junit:4.12' 26 | androidTestImplementation 'com.android.support.test:runner:1.0.1' 27 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1' 28 | implementation project(':rkmediacodec') 29 | } 30 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/rockchip/rkmediacodecdemo/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.rockchip.rkmediacodecdemo; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumented test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() throws Exception { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("com.rockchip.rkmediacodecdemo", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /app/src/main/java/com/rockchip/rkmediacodecdemo/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.rockchip.rkmediacodecdemo; 2 | 3 | import android.os.Bundle; 4 | import android.support.v7.app.AppCompatActivity; 5 | import android.util.Log; 6 | import android.view.View; 7 | import android.widget.Button; 8 | 9 | public class MainActivity extends AppCompatActivity { 10 | Button start_button; 11 | private PlayerView mSurfaceView; 12 | private PlayerView mSurfaceView2; 13 | private PlayerView mSurfaceView3; 14 | private PlayerView mSurfaceView4; 15 | // private String RTSP_IP1 = "172.16.9.113"; 16 | private String RTSP_IP1 = "192.168.17.102"; 17 | private String RTSP_IP2 = "192.168.17.101"; 18 | private String RTSP_IP3 = "192.168.17.105"; 19 | private String RTSP_IP4 = "192.168.17.106"; 20 | private int RTSP_port2 = 8554; 21 | private int RTSP_port = 554; 22 | private String RTSP_func2 = "video"; 23 | private String RTSP_func = "live/av0"; 24 | private String RTSP_func_av1 = "live/av0"; 25 | 26 | @Override 27 | protected void onCreate(Bundle savedInstanceState) { 28 | super.onCreate(savedInstanceState); 29 | setContentView(R.layout.activity_main); 30 | mSurfaceView = (PlayerView) findViewById(R.id.surfaceView1); 31 | mSurfaceView2 = (PlayerView) findViewById(R.id.surfaceView2); 32 | mSurfaceView3 = (PlayerView) findViewById(R.id.surfaceView3); 33 | mSurfaceView4 = (PlayerView) findViewById(R.id.surfaceView4); 34 | 35 | start_button = (Button) findViewById(R.id.start_button); 36 | start_button.setOnClickListener(new View.OnClickListener() { 37 | @Override 38 | public void onClick(View view) { 39 | Log.d("*******", "onClick: ----------------------------------"); 40 | mSurfaceView.MediaPaly(RTSP_IP3, RTSP_port, RTSP_func_av1); 41 | // mSurfaceView.MediaPaly(RTSP_IP1,RTSP_port,RTSP_func); 42 | mSurfaceView2.MediaPaly(RTSP_IP4, RTSP_port, RTSP_func_av1); 43 | // mSurfaceView2.MediaPaly(RTSP_IP2, RTSP_port, RTSP_func); 44 | mSurfaceView3.MediaPaly(RTSP_IP3,RTSP_port,RTSP_func); 45 | mSurfaceView4.MediaPaly(RTSP_IP4,RTSP_port,RTSP_func); 46 | } 47 | }); 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /app/src/main/java/com/rockchip/rkmediacodecdemo/PlayerView.java: -------------------------------------------------------------------------------- 1 | package com.rockchip.rkmediacodecdemo; 2 | 3 | import android.content.Context; 4 | import android.opengl.GLES20; 5 | import android.util.AttributeSet; 6 | import android.util.Log; 7 | import android.view.SurfaceHolder; 8 | import android.view.SurfaceView; 9 | 10 | /** 11 | * Created by cxh on 2018/1/3 12 | * E-mail: shon.chen@rock-chips.com 13 | */ 14 | 15 | public class PlayerView extends SurfaceView implements SurfaceHolder.Callback{ 16 | 17 | static final String TAG = "PlayerView"; 18 | Context mContext; 19 | private RKMediaplayer mRKMediaplayer = null; 20 | private SurfaceHolder mSurfaceHolder; 21 | public PlayerView(Context context) { 22 | super(context); 23 | mContext = context; 24 | init(); 25 | } 26 | 27 | private void init() { 28 | mSurfaceHolder = getHolder(); 29 | mSurfaceHolder.addCallback(this); 30 | } 31 | public PlayerView(Context context, AttributeSet attrs) { 32 | super(context, attrs); 33 | mContext = context; 34 | init(); 35 | } 36 | 37 | public PlayerView(Context context, AttributeSet attrs, int defStyleAttr) { 38 | super(context, attrs, defStyleAttr); 39 | mContext = context; 40 | init(); 41 | } 42 | 43 | 44 | public String getfps(){ 45 | if (mRKMediaplayer != null) { 46 | return mRKMediaplayer.getfps(); 47 | }else { 48 | Log.d(TAG, "MediaPaly: NULLLLLLLLLLLLLLLLLLLLLLLLLLLL"); 49 | } 50 | return null; 51 | } 52 | 53 | /** 54 | * 以下为播放器状态控制代码 55 | */ 56 | public void MediaPaly(String RTSP, int RTSPport, String RTSPfunc) { 57 | //初始化原本应该得放在OpenGL创建的时候一起的,但是如果在OpenGL初始化的时候创建就会出现闪退的现象 58 | //GLSurfaceView_num > 1 说明是只有一个播放窗口需要为其添加一个播放器 59 | if (mRKMediaplayer == null ){ 60 | mRKMediaplayer = new RKMediaplayer(mSurfaceHolder); 61 | } 62 | mRKMediaplayer.playRTSPVideo(RTSP, RTSPport, RTSPfunc); 63 | } 64 | 65 | public void MediaStop() { 66 | if (mRKMediaplayer != null) { 67 | GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f); 68 | mRKMediaplayer.stop(); 69 | } 70 | } 71 | 72 | public void MediaPause() { 73 | if (mRKMediaplayer != null) { 74 | } 75 | } 76 | 77 | public void MediaContinue() { 78 | if (mRKMediaplayer != null) { 79 | } 80 | } 81 | 82 | @Override 83 | public void surfaceCreated(SurfaceHolder surfaceHolder) { 84 | 85 | } 86 | 87 | @Override 88 | public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i1, int i2) { 89 | mRKMediaplayer = new RKMediaplayer(mSurfaceHolder); 90 | // mRKPlayer = new RKMediaplayer(surfaceHolder, 0); 91 | 92 | } 93 | 94 | @Override 95 | public void surfaceDestroyed(SurfaceHolder surfaceHolder) { 96 | mRKMediaplayer.stop(); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /app/src/main/java/com/rockchip/rkmediacodecdemo/PortScanner.java: -------------------------------------------------------------------------------- 1 | package com.rockchip.rkmediacodecdemo; 2 | 3 | 4 | import android.os.Handler; 5 | import android.util.Log; 6 | 7 | import java.io.IOException; 8 | import java.net.InetAddress; 9 | import java.net.InetSocketAddress; 10 | import java.net.InterfaceAddress; 11 | import java.net.NetworkInterface; 12 | import java.net.Socket; 13 | import java.net.SocketAddress; 14 | import java.net.SocketException; 15 | import java.util.Enumeration; 16 | 17 | /** 18 | * Created by cxh on 2017/7/31. 19 | */ 20 | 21 | public class PortScanner { 22 | 23 | private static final String TAG = "ContentValues"; 24 | private static final boolean netdebug = false; 25 | private static final boolean DEBUG = true; 26 | private static int bindPort = 33333; 27 | Handler handler; 28 | 29 | public PortScanner() { 30 | } 31 | 32 | /** 33 | * 传入当前的handler 34 | * 35 | * @param handler handler 36 | */ 37 | 38 | 39 | /** 40 | * 获取本机的IP 41 | * 42 | * @return Ip地址 43 | */ 44 | public String getLocalHostIP() { 45 | String ip; 46 | try { 47 | /**返回本地主机。*/ 48 | InetAddress addr = InetAddress.getLocalHost(); 49 | /**返回 IP 地址字符串(以文本表现形式)*/ 50 | ip = addr.getHostAddress(); 51 | } catch (Exception ex) { 52 | ip = ""; 53 | } 54 | return ip; 55 | } 56 | 57 | /** 58 | * 获取广播地址 59 | * 60 | * @return 广播地址 61 | */ 62 | public String getBroadcast() throws SocketException { 63 | System.setProperty("java.net.preferIPv4Stack", "true"); 64 | for (Enumeration niEnum = NetworkInterface.getNetworkInterfaces(); niEnum.hasMoreElements(); ) { 65 | NetworkInterface ni = niEnum.nextElement(); 66 | if (!ni.isLoopback()) { 67 | for (InterfaceAddress interfaceAddress : ni.getInterfaceAddresses()) { 68 | if (interfaceAddress.getBroadcast() != null) { 69 | return interfaceAddress.getBroadcast().toString().substring(1); 70 | } 71 | } 72 | } 73 | } 74 | return ""; 75 | } 76 | 77 | 78 | 79 | /** 80 | * 寻找一个可用的端口 81 | * 82 | * @return 可用的端口号 83 | */ 84 | public synchronized static int StartLocalPort() { 85 | int AvailablePort = bindPort; 86 | while (true) { 87 | bindPort++; 88 | AvailablePort = bindPort; 89 | if (isPortAvailable(bindPort)) ; 90 | { 91 | bindPort++; 92 | if (isPortAvailable(bindPort)) ; 93 | { 94 | break; 95 | } 96 | } 97 | } 98 | bindPort += 100; 99 | return AvailablePort; 100 | } 101 | 102 | /** 103 | * 绑定本机指定端口和IP地址(绑定失败抛出异常) 104 | * 105 | * @param host 待扫描IP 106 | * @param port 待扫描端口 107 | */ 108 | private static void bindPort(String host, int port) throws Exception { 109 | Socket s = new Socket(); 110 | s.bind(new InetSocketAddress(host, port)); 111 | s.close(); 112 | } 113 | 114 | /** 115 | * 检测本机传入的端口是否被占用 116 | * 117 | * @param port 待扫描端口 118 | * @return 是否被占用 119 | */ 120 | private static boolean isPortAvailable(int port) { 121 | try { 122 | bindPort("0.0.0.0", port); 123 | bindPort(InetAddress.getLocalHost().getHostAddress(), port); 124 | return true; 125 | } catch (Exception e) { 126 | return false; 127 | } 128 | } 129 | 130 | /** 131 | * 检测将要连接的IP端口是否可用 132 | * 133 | * @param ip 待加测的IP 134 | * @param Port 待检测的端口 135 | * @param timeout 连接超时时间 136 | * @return 是否被占用 137 | */ 138 | private boolean TCPPortIsUsable(String ip, int Port, int timeout) { 139 | 140 | try { 141 | Socket socket; 142 | SocketAddress socketAddress; 143 | 144 | socket = new Socket(); 145 | socketAddress = new InetSocketAddress(ip, Port); 146 | socket.connect(socketAddress, timeout); 147 | socket.close(); 148 | } catch (IOException e) { 149 | Log.d(TAG, "IP: " + ip + " 端口:" + Port + " 关闭"); 150 | return false; 151 | } 152 | return true; 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /app/src/main/java/com/rockchip/rkmediacodecdemo/rtsp/AACPackage.java: -------------------------------------------------------------------------------- 1 | package com.rockchip.rkmediacodecdemo.rtsp; 2 | 3 | import java.io.IOException; 4 | 5 | /** 6 | * Created by zhanghao on 2017/1/6. 7 | */ 8 | 9 | public class AACPackage { 10 | public byte[] ADTS = {(byte)0xFF, (byte)0xF1, 0x00, 0x00, 0x00, 0x00, (byte)0xFC}; 11 | public int AU_HEADER_LENGTH = 2; 12 | public byte[] data; 13 | 14 | public AACPackage() { 15 | 16 | } 17 | 18 | public AACPackage(int samplerate, int channel, int bit) { 19 | switch(samplerate) 20 | { 21 | case 16000: 22 | ADTS[2] = 0x60; 23 | break; 24 | case 32000: 25 | ADTS[2] = 0x54; 26 | break; 27 | case 44100: 28 | ADTS[2] = 0x50; 29 | break; 30 | case 48000: 31 | ADTS[2] = 0x4C; 32 | break; 33 | case 96000: 34 | ADTS[2] = 0x40; 35 | break; 36 | default: 37 | break; 38 | } 39 | ADTS[3] = (channel==2)?(byte)0x80:0x40; 40 | } 41 | 42 | public AACPackage getCompleteAACPackage(RTPPackage rtp) throws IOException { 43 | AACPackage aac = new AACPackage(44100, 2, 16); // TODO:: audio format 44 | aac.AU_HEADER_LENGTH = 2;//(rtp.csrc[0]&0xFF)<<8 | (rtp.csrc[1]&0xFF); 45 | //if (aac.AU_HEADER_LENGTH != 2) throw new IOException("unknown AU_HEADER_LENGTH!" + rtp.csrc[0] + ":" + rtp.csrc[1]); 46 | final int len = rtp.csrc.length - 2 - aac.AU_HEADER_LENGTH + aac.ADTS.length; 47 | int plen = (len << 5)|0x1F; 48 | aac.ADTS[4] = (byte)(plen >> 8); 49 | aac.ADTS[5] = (byte)(plen&0xFF); 50 | //aac.data = new byte[len]; 51 | //System.arraycopy(aac.ADTS, 0, aac.data, 0, aac.ADTS.length); 52 | //System.arraycopy(rtp.csrc, 2 + aac.AU_HEADER_LENGTH, aac.data, aac.ADTS.length, rtp.csrc.length - 2 - aac.AU_HEADER_LENGTH); 53 | 54 | aac.data = new byte[len-aac.ADTS.length]; 55 | System.arraycopy(rtp.csrc, 2 + aac.AU_HEADER_LENGTH, aac.data, 0, rtp.csrc.length - 2 - aac.AU_HEADER_LENGTH); 56 | return aac; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /app/src/main/java/com/rockchip/rkmediacodecdemo/rtsp/H264Package.java: -------------------------------------------------------------------------------- 1 | package com.rockchip.rkmediacodecdemo.rtsp; 2 | 3 | import android.util.Log; 4 | 5 | import java.util.ArrayDeque; 6 | import java.util.ArrayList; 7 | import java.util.List; 8 | import java.util.Queue; 9 | 10 | /** 11 | * Created by zhanghao on 2017/1/2. 12 | */ 13 | 14 | public class H264Package { 15 | private static final String TAG = "H264Package"; 16 | H264Package sLastH264Worker = null; 17 | List mNALU = null; 18 | Queue sCompleteH264Frame = new ArrayDeque<>(3); 19 | 20 | public H264Package() { 21 | mNALU = new ArrayList<>(); 22 | } 23 | 24 | private void append(NALUnit unit) { 25 | mNALU.add(unit); 26 | } 27 | 28 | private boolean checkSN() { 29 | long last_sn = -1; 30 | for(int i=0; i. sendNALUnit: " + unit.first_mb_in_slice); 47 | //H264Package tmp = null; 48 | if (unit.nal_unit_type == 31) { /* Rockchip End Frame */ 49 | // if (DefaultConfig.DEFAULT_DEBUG_DELAY) 50 | // Log.d(DefaultConfig.TAG, "----------------- END FRAME ----------------"); 51 | // if(sLastH264Worker != null) { 52 | // sCompleteH264Frame.add(sLastH264Worker); 53 | // } 54 | sLastH264Worker = null; 55 | } else if (unit.first_mb_in_slice == 0) { 56 | //tmp = sLastH264Worker; 57 | if(sLastH264Worker != null) { 58 | sCompleteH264Frame.add(sLastH264Worker); 59 | //Log.d("Rockchip", ">.0 sCompleteH264Frame add " + sLastH264Worker); 60 | } 61 | sLastH264Worker = new H264Package(); 62 | sLastH264Worker.append(unit); 63 | } else if (unit.first_mb_in_slice == -1) { 64 | //ret = sLastH264Worker; 65 | if(sLastH264Worker != null) { 66 | sCompleteH264Frame.add(sLastH264Worker); 67 | //Log.d("Rockchip", ">.-1 sCompleteH264Frame add " + sLastH264Worker); 68 | } 69 | sLastH264Worker = new H264Package(); 70 | sLastH264Worker.append(unit); 71 | sCompleteH264Frame.add(sLastH264Worker); 72 | //Log.d("Rockchip", ">.-1+ sCompleteH264Frame add " + sLastH264Worker); 73 | sLastH264Worker = null; 74 | } else { 75 | if (sLastH264Worker != null) 76 | sLastH264Worker.append(unit); 77 | else 78 | Log.w(TAG, "Got a broken 264 package !"); 79 | } 80 | } 81 | 82 | public H264Package getCompleteH264() { 83 | H264Package ret = sCompleteH264Frame.poll(); 84 | //Log.d("Rockchip", ">. getCompleteH264 poll: " + ret); 85 | return ret; 86 | } 87 | 88 | 89 | public byte[] getBytes() { 90 | int size = 0; 91 | for (NALUnit unit: mNALU) { 92 | size += unit.getLength(); 93 | } 94 | 95 | byte[] bytes = new byte[size]; 96 | int offset = 0; 97 | for (int i=0; i mFUADataWorker = null; 37 | /* 38 | * 取一段码流分析如下: 39 | 80 60 01 0f 00 0e 10 00 00 00 00 00 7c 85 88 82 €`..........|??? 40 | 00 0a 7f ca 94 05 3b 7f 3e 7f fe 14 2b 27 26 f8 ...??.;.>.?.+'&? 41 | 89 88 dd 85 62 e1 6d fc 33 01 38 1a 10 35 f2 14 ????b?m?3.8..5?. 42 | 84 6e 21 24 8f 72 62 f0 51 7e 10 5f 0d 42 71 12 ?n!$?rb?Q~._.Bq. 43 | 17 65 62 a1 f1 44 dc df 4b 4a 38 aa 96 b7 dd 24 .eb??D??KJ8????$ 44 | 45 | 前12字节是RTP Header 46 | 7c是FU indicator 47 | 85是FU Header 48 | FU indicator(0x7C)和FU Header(0x85)换成二进制如下 49 | 0111 1100 1000 0101 50 | 按顺序解析如下: 51 | 0           是F 52 | 11          是NRI 53 | 11100       是FU Type,这里是28,即FU-A 54 | 55 | 1         是S,Start,说明是分片的第一包 56 | 0           是E,End,如果是分片的最后一包,设置为1,这里不是 57 | 0           是R,Remain,保留位,总是0 58 | 00101        是NAl Type,这里是5,说明是关键帧 59 | * */ 60 | /* 分片单元指示 61 | NALU_HEADER 62 | +---------------+ 63 | |0|1|2|3|4|5|6|7| 64 | +-+-+-+-+-+-+-+-+ 65 | |F|NRI| Type | 66 | +---------------+*/ 67 | 68 | /* 分片单元头 69 | FU_HEADER 70 | +---------------+ 71 | |0|1|2|3|4|5|6|7| 72 | +-+-+-+-+-+-+-+-+ 73 | |S|E|R| Type | 74 | +---------------+*/ 75 | public NALUnit parseHeader(RTPPackage rtp) throws IOException { 76 | NALUnit unit = new NALUnit(); 77 | unit.nal_unit_type = rtp.csrc[0]&0x1F; 78 | 79 | if (unit.nal_unit_type == NALU_HEADER_TYPE_SLICE_NON_IDR || 80 | unit.nal_unit_type == NALU_HEADER_TYPE_SLICE_IDR) { 81 | unit.first_mb_in_slice = getExpGolomb(rtp.csrc[1]); 82 | } else if (unit.nal_unit_type == NALU_HEADER_TYPE_FU_A) { 83 | unit.fu_isStart = (rtp.csrc[1]&0x80) != 0; 84 | unit.fu_isEnd = (rtp.csrc[1]&0x40) !=0; 85 | unit.fua_nal_unit_type = rtp.csrc[1]&0x1F; 86 | if (unit.fua_nal_unit_type == NALU_HEADER_TYPE_SLICE_NON_IDR || 87 | unit.fua_nal_unit_type == NALU_HEADER_TYPE_SLICE_IDR) { 88 | unit.fua_first_mb_in_slice = getExpGolomb(rtp.csrc[2]); 89 | } 90 | } 91 | return unit; 92 | } 93 | 94 | private void filldata(byte[] buf, int startFlagLength) throws IOException { 95 | data = new byte[buf.length + startFlagLength]; 96 | for (int f=0; f(); 113 | mFUADataWorker.add(unit); 114 | return null; 115 | } else if (unit.fu_isEnd && !unit.fu_isStart) { 116 | if (mFUADataWorker == null) { 117 | return null; 118 | } 119 | mFUADataWorker.add(unit); 120 | 121 | int length = 5; // add 4byte start flag + NALU_header 122 | for (NALUnit b: mFUADataWorker) { 123 | length += (b.data.length-2); // reduce NALU_header & fua_header 124 | } 125 | 126 | // Log.d(TAG, "complexUnit.data.length:" + length + " mFUADataWorker.get(0).data[].length" + mFUADataWorker.get(0).data.length); 127 | NALUnit complexUnit = new NALUnit(); 128 | complexUnit.nal_unit_type = unit.fua_nal_unit_type; 129 | complexUnit.first_mb_in_slice = mFUADataWorker.get(0).fua_first_mb_in_slice; 130 | complexUnit.data = new byte[length]; 131 | complexUnit.data[0] = 0x0; 132 | complexUnit.data[1] = 0x0; 133 | complexUnit.data[2] = 0x0; 134 | complexUnit.data[3] = 0x1; 135 | complexUnit.data[4] = (byte)((mFUADataWorker.get(0).data[0] & 0xE0) | (mFUADataWorker.get(0).data[1] & 0x1F)); 136 | //Log.d("Rockchip", ">> fua type:"+unit.fua_nal_unit_type + " ; 4: " + complexUnit.data[4]); 137 | complexUnit.sequece_number = ++sNALU_Sequece_Number; // set sNALU_Sequece_Number 138 | //Log.d(TAG," complexUnit.sequece_number = " + complexUnit.sequece_number); 139 | 140 | int offset = 5; 141 | long checkSequeceNum = -1; 142 | for (int i=0; i 0 && unit.nal_unit_type < 13) { 167 | return create(rtp); 168 | } else if (unit.nal_unit_type == 31){ /* Rockchip End Frame Flag */ 169 | return create(rtp); 170 | } else { 171 | throw new IOException("unknown nalu type :"+unit.nal_unit_type); 172 | } 173 | } 174 | 175 | private NALUnit create(RTPPackage rtp) throws IOException { 176 | NALUnit unit = parseHeader(rtp); 177 | unit.sequece_number = ++sNALU_Sequece_Number; // set sNALU_Sequece_Number 178 | //Log.d(TAG," unit.sequece_number = " + unit.sequece_number); 179 | 180 | if (unit.first_mb_in_slice > 0){ 181 | unit.filldata(rtp.csrc, NALU_START_FLAG_3); 182 | } else { /* 0 or -1*/ 183 | unit.filldata(rtp.csrc, NALU_START_FLAG_4); 184 | } 185 | return unit; 186 | } 187 | 188 | public int getLength() { 189 | return data.length; 190 | } 191 | 192 | public int getExpGolomb(byte buf) { 193 | return (buf & 0x80) != 0 ? 0 : 1; 194 | } 195 | } 196 | -------------------------------------------------------------------------------- /app/src/main/java/com/rockchip/rkmediacodecdemo/rtsp/RTCPPackage.java: -------------------------------------------------------------------------------- 1 | package com.rockchip.rkmediacodecdemo.rtsp; 2 | 3 | import java.util.Random; 4 | 5 | /** 6 | * Created by zhanghao on 2017/1/3. 7 | */ 8 | 9 | public class RTCPPackage { 10 | private static final String TAG = "RTCPPackage"; 11 | 12 | public static final byte RTCP_PAYLOAD_TYPE_SR = (byte)200; // Sender Report 13 | public static final byte RTCP_PAYLOAD_TYPE_RR = (byte)201; // Receiver Report 14 | public static final byte RTCP_PAYLOAD_TYPE_SDES = (byte)202; // Source Description 15 | public static final byte RTCP_PAYLOAD_TYPE_BYE = (byte)203; // Say Goodbye 16 | 17 | private byte mVP = (byte)0x80; // & 0xE0; // 1110 0000 18 | private byte mRC = (byte)0x01; // & 0x1F; // 0001 1111 19 | private byte mPT = 0; 20 | private byte mLength = 0; 21 | private int mHighestSequenceNum = 0; 22 | public long mLastSR = 0; 23 | public long mDelayLastSR = 0; 24 | public long mNtpTimestamp = 0; 25 | 26 | private int mSSRC0 = -1; // sender ssrc 27 | private int mSSRC1 = 0; 28 | 29 | public RTCPPackage create(byte pt, int ssrc, int lastsn, long lsr, long dlsr) { 30 | mSSRC1 = ssrc; 31 | RTCPPackage packet = new RTCPPackage(); 32 | packet.mPT = pt; 33 | packet.mHighestSequenceNum = lastsn; 34 | packet.mLastSR = lsr; 35 | packet.mDelayLastSR = dlsr; 36 | 37 | if (mSSRC0 == -1) { 38 | Random rnd = new Random(System.currentTimeMillis()); 39 | mSSRC0 = rnd.nextInt(); 40 | } 41 | 42 | return packet; 43 | } 44 | 45 | public RTCPPackage parseSR(byte[] sr){ 46 | RTCPPackage packet = new RTCPPackage(); 47 | if(sr.length >= 14) 48 | packet.mNtpTimestamp = ((sr[10]&0xFF)<<24) | ((sr[11]&0xFF)<<16) | ((sr[12]&0xFF)<<8) | (sr[13]&0xFF); 49 | //if(sr.length >= 48) 50 | // packet.mLastSR = sr[44]<<24 | sr[45]<<16 | sr[46]<<8 | sr[47]; 51 | //if(sr.length >= 52) 52 | // packet.mDelayLastSR = sr[48]<<24 | sr[49]<<16 | sr[50]<<8 | sr[51]; 53 | return packet; 54 | } 55 | 56 | /* Sendor Report 57 | 0 1 2 3 58 | 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 59 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 60 | |V=2|P| RC | PT | length | 3 61 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 62 | | SSRC of sender | 7 63 | +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ 64 | | NTP timestamp, most significant word | 11 65 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 66 | | NTP timestamp, least significant word | 15 67 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 68 | | RTP timestampe | 19 69 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 70 | | sender's packet count | 23 71 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 72 | | sender's octet count | 27 73 | +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ 74 | | SSRC_1 | 31 75 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 76 | | fraction lost | cumulative number of packets lost | 35 77 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 78 | | extended highest sequence number received | 39 79 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 80 | | interarrival jitter | 43 81 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 82 | | last SR (LSR) | 47 83 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 84 | | delay since last SR (DLSR) | 51 85 | +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ 86 | */ 87 | 88 | /* Receiver Report 89 | 0 1 2 3 90 | 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 91 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 92 | |V=2|P| RC | PT | length | 3 93 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 94 | | SSRC of sender | 7 95 | +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ 96 | | SSRC_1 | 11 97 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 98 | | fraction lost | cumulative number of packets lost | 15 99 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 100 | | extended highest sequence number received | 19 101 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 102 | | interarrival jitter | 23 103 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 104 | | last SR (LSR) | 27 105 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 106 | | delay since last SR (DLSR) | 31 107 | +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ 108 | */ 109 | 110 | /* Source Description 111 | 0 1 2 3 112 | 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 113 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 114 | |V=2|P| RC | PT | length | 3 115 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 116 | | SSRC / CSRC_1 | 7 117 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 118 | | SDES items | 11 119 | | ... | 120 | +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ 121 | | ... | 122 | +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ 123 | 124 | Items: 125 | 0 1 2 3 126 | 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 127 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 128 | | ITEM | length | content ... 129 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 130 | 131 | CNAME=1 132 | NAME=2 133 | EMAIL=3 134 | PHONE=4 135 | LOC=5 // Location 136 | TOOL=6 // Client Tool 137 | NOTE=7 138 | PRIV=8 139 | */ 140 | 141 | /* BYE 142 | 0 1 2 3 143 | 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 144 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 145 | |V=2|P| RC | PT | length | 3 146 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 147 | | SSRC / CSRC_1 | 7 148 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 149 | | ... | 150 | +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ 151 | | length | reason for leaving | 152 | +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ 153 | */ 154 | 155 | 156 | public byte[] getRRBytes() { 157 | // Head : 158 | // SR/RR = 8byte 159 | // SDES = 4byte 160 | byte[] bytes = new byte[32]; 161 | bytes[0] = (byte)((mVP&0xE0)|(mRC&0x1F)); 162 | bytes[1] = mPT; 163 | bytes[2] = 0; // Length H16 164 | bytes[3] = (byte)(bytes.length/4-1); // Length L16 (32bit=1) 165 | 166 | // SSRC 167 | bytes[4] = (byte)((mSSRC0&0xFF000000) >> 24); 168 | bytes[5] = (byte)((mSSRC0&0x00FF0000) >> 16); 169 | bytes[6] = (byte)((mSSRC0&0x0000FF00) >> 8); 170 | bytes[7] = (byte)((mSSRC0&0x000000FF)); 171 | 172 | // SSRC 1 173 | bytes[8] = (byte)((mSSRC1&0xFF000000) >> 24); 174 | bytes[9] = (byte)((mSSRC1&0x00FF0000) >> 16); 175 | bytes[10] = (byte)((mSSRC1&0x0000FF00) >> 8); 176 | bytes[11] = (byte)((mSSRC1&0x000000FF)); 177 | 178 | // fraction lost 179 | bytes[12] = 0; 180 | // cumulative number of packets lost 181 | bytes[13] = (byte)0xFF; 182 | bytes[14] = (byte)0xFF; 183 | bytes[15] = (byte)0xFF; 184 | 185 | // extended highest sequence number received 186 | bytes[16] = (byte)((mHighestSequenceNum&0xFF000000) >> 24); 187 | bytes[17] = (byte)((mHighestSequenceNum&0x00FF0000) >> 16); 188 | bytes[18] = (byte)((mHighestSequenceNum&0x0000FF00) >> 8); 189 | bytes[19] = (byte)((mHighestSequenceNum&0x000000FF)); 190 | 191 | // interarrival jitter // TODO:: 192 | bytes[20] = 0x0; 193 | bytes[21] = 0x0; 194 | bytes[22] = 0x0; 195 | bytes[23] = 0x3E; 196 | 197 | // last SR 198 | bytes[24] = (byte)((mLastSR&0xFF000000) >> 24); 199 | bytes[25] = (byte)((mLastSR&0x00FF0000) >> 16); 200 | bytes[26] = (byte)((mLastSR&0x0000FF00) >> 8); 201 | bytes[27] = (byte)((mLastSR&0x000000FF)); 202 | 203 | // delay since last SR 204 | bytes[28] = (byte)((mDelayLastSR&0xFF000000) >> 24); 205 | bytes[29] = (byte)((mDelayLastSR&0x00FF0000) >> 16); 206 | bytes[30] = (byte)((mDelayLastSR&0x0000FF00) >> 8); 207 | bytes[31] = (byte)((mDelayLastSR&0x000000FF)); 208 | return bytes; 209 | } 210 | 211 | public byte[] getSDESBytes() { 212 | // TODO:: 213 | byte[] bytes = new byte[32]; 214 | bytes[0] = (byte)0x80; 215 | bytes[1] = RTCP_PAYLOAD_TYPE_SDES; 216 | bytes[2] = 0; // Length H16 217 | bytes[3] = (byte)(bytes.length/4-1); // Length L16 (32bit=1) 218 | return bytes; 219 | } 220 | 221 | public byte[] getBYEBytes() { 222 | byte[] bytes = new byte[8]; 223 | bytes[0] = (byte)((mVP&0xE0)|(mRC&0x1F)); 224 | bytes[1] = mPT; 225 | bytes[2] = 0; // Length H16 226 | bytes[3] = (byte)(bytes.length/4-1); // Length L16 (32bit=1) 227 | 228 | // SSRC 229 | bytes[4] = (byte)((mSSRC0&0xFF000000) >> 24); 230 | bytes[5] = (byte)((mSSRC0&0x00FF0000) >> 16); 231 | bytes[6] = (byte)((mSSRC0&0x0000FF00) >> 8); 232 | bytes[7] = (byte)((mSSRC0&0x000000FF)); 233 | return bytes; 234 | } 235 | } 236 | -------------------------------------------------------------------------------- /app/src/main/java/com/rockchip/rkmediacodecdemo/rtsp/RTPConnector.java: -------------------------------------------------------------------------------- 1 | package com.rockchip.rkmediacodecdemo.rtsp; 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 | * Created by zhanghao on 2016/12/29. 16 | */ 17 | 18 | public class RTPConnector { 19 | private static final String TAG = "RTPConnector"; 20 | private static final boolean DEBUG_NALU = false; 21 | 22 | private RTPType mRTPType = null; 23 | private RTPThread mRTPThread = null; 24 | private RTCPThread mRTCPThread = null; 25 | 26 | private boolean mIsTCP = false; 27 | private String mServerIP = ""; 28 | private int mClientPort = 0; 29 | private int mServerPort = 0; 30 | private byte[] message = new byte[2048]; 31 | 32 | private long mLastSequenceNum = -1; 33 | private int mServerSSRC = -1; 34 | private long mLastSR = 0; 35 | private long mLastTimestamp = 0; 36 | private long mLast264Timestamp = 0; 37 | private long mFirstPackageTimestamp = 0; 38 | private long mH264FrameDelay = 0; 39 | private long mH264FrameInterval = 0; 40 | 41 | private int mLostPackageCount = 0; 42 | private int mRecvPackageCount = 0; 43 | private long mLastestSequenceTime = 0; 44 | private long mBps = 0; 45 | private int[] mNaluTypeCount = {0,0,0,0,0,0,0,0,0,0}; 46 | 47 | private OnFrameListener mOnFrameCallback = null; 48 | 49 | public enum RTPType { VIDEO, AUDIO } 50 | 51 | public RTPConnector(boolean isTCP, int clientPort, String serverIP, int serverPort, RTPType rtpType) { 52 | mIsTCP = isTCP; 53 | mClientPort = clientPort; 54 | mServerIP = serverIP; 55 | mServerPort = serverPort; 56 | mRTPType = rtpType; 57 | } 58 | 59 | public void connect() { 60 | 61 | if(mIsTCP) { 62 | Log.e(TAG, "unsupport tcp yet !"); 63 | } else { 64 | mRTPThread = new RTPThread(mClientPort, mServerIP, mServerPort); 65 | mRTCPThread = new RTCPThread(mClientPort+1, mServerIP, mServerPort+1); 66 | mRTPThread.start(); 67 | mRTCPThread.start(); 68 | } 69 | } 70 | 71 | public void disconnect() { 72 | if (mRTPThread == null || mRTCPThread == null) 73 | return; // Already Disconnected 74 | Log.w(TAG, "RTP Connector disconnecting ..."); 75 | mRTCPThread.sendReciveReport(RTCPPackage.RTCP_PAYLOAD_TYPE_BYE); 76 | try { 77 | mRTPThread.join(); 78 | mRTCPThread.join(); 79 | mRTPThread = null; 80 | mRTCPThread = null; 81 | } catch (InterruptedException e) { 82 | e.printStackTrace(); 83 | } 84 | } 85 | 86 | public int getLostPackageCount() { 87 | return mLostPackageCount; 88 | } 89 | public int getRecvPackageCount() {return mRecvPackageCount;} 90 | public long getLastestSequeceTime() {return mLastestSequenceTime;} 91 | public long getH264FrameDelay() {long r = mH264FrameDelay;mH264FrameDelay = 0;return r;} 92 | public long getH264FrameInterval() {long r = mH264FrameInterval;mH264FrameInterval = 0;return r;} 93 | public long getH264Bps() {long r=mBps;mBps=0;return r;} 94 | public int[] getH264NaluTypeCount() {int[] r=mNaluTypeCount;mNaluTypeCount= new int[10];return r;} 95 | public void resetPackageCount() {mLostPackageCount = 0;mRecvPackageCount = 0;} 96 | 97 | private class RTPThread extends Thread { 98 | private int mLocalPort = 0; 99 | private int mRemotePort = 0; 100 | private String mSerIP = ""; 101 | private volatile boolean mIsStop = false; 102 | private long recordTime = 0; 103 | private boolean mDbgIsComplete = false; 104 | 105 | private DatagramSocket mSocket; 106 | private DatagramPacket mPacket; 107 | 108 | 109 | public RTPThread(int localPort, String serverip, int serverPort){ 110 | mLocalPort = localPort; 111 | mRemotePort = serverPort; 112 | mSerIP = serverip; 113 | 114 | try { 115 | mSocket = new DatagramSocket(mClientPort); 116 | mPacket = new DatagramPacket(message,message.length); 117 | } catch (SocketException e) { 118 | e.printStackTrace(); 119 | } 120 | } 121 | 122 | public void exit(){ 123 | Log.w(TAG, "RTP thread exiting ..."); 124 | mIsStop = true; 125 | } 126 | 127 | public void disconnect() { 128 | mSocket.disconnect(); 129 | mSocket.close(); 130 | } 131 | 132 | @Override 133 | public void run() { 134 | RTPPackage rtppkg =new RTPPackage().create(mPacket.getData(), mPacket.getLength()); 135 | // int size = 170*1024; 136 | while(!mIsStop) { 137 | long currentTime; 138 | try { 139 | if (mSocket.isClosed()) 140 | return; 141 | mSocket.receive(mPacket); 142 | // if(DefaultConfig.DEFAULT_DEBUG_DELAY) Log.d(DefaultConfig.TAG, "1. recv a package: size:" + mPacket.getLength() + " : " + System.currentTimeMillis()); 143 | rtppkg =rtppkg.create(mPacket.getData(), mPacket.getLength()); 144 | ++mRecvPackageCount; 145 | mBps += mPacket.getLength(); 146 | mLastestSequenceTime = rtppkg.timestamp; 147 | if (mServerSSRC != rtppkg.ssrc) 148 | mServerSSRC = rtppkg.ssrc; 149 | if(DEBUG_NALU) Log.d(TAG, "got rtp len:" + mPacket.getLength() + ";" + rtppkg.toString()); 150 | if (rtppkg.sequenceNumber != mLastSequenceNum + 1 && mLastSequenceNum != -1) { 151 | Log.w(TAG, "lost RTP package !! last:" + mLastSequenceNum + " ; recv:"+rtppkg.sequenceNumber); 152 | // if(DefaultConfig.DEFAULT_DEBUG_DELAY) Log.e(DefaultConfig.TAG, "x. lost RTP package !! last:" + mLastSequenceNum + " ; recv:"+rtppkg.sequenceNumber); 153 | mLostPackageCount += (rtppkg.sequenceNumber-mLastSequenceNum-1); 154 | } 155 | mLastSequenceNum = rtppkg.sequenceNumber; 156 | //有些负载类型由于诞生的较晚,没有具体的PT值,只能使用动态(dynamic)PT值,即96到127,这就是为什么大家普遍指定H264的PT值为96。 157 | if (rtppkg.ptype >= RTPPackage.PTYPE_DYNAMIC_MIN && rtppkg.ptype <= RTPPackage.PTYPE_DYNAMIC_MAX) { 158 | // Log.d(TAG, "run: mRTPType = " +mRTPType); 159 | if(mRTPType!=null) 160 | switch (mRTPType) { 161 | case VIDEO: 162 | recoverH264Package(rtppkg); 163 | break; 164 | case AUDIO: 165 | recoverAACPackage(rtppkg); 166 | break; 167 | } 168 | } else { 169 | Log.w(TAG, "got a unknowable RTP package, type=" + rtppkg.ptype); 170 | } 171 | 172 | //RTCP START : every 10s send a rtcp packet to server 173 | currentTime = System.currentTimeMillis(); 174 | if(currentTime - recordTime > 10000) { 175 | recordTime = currentTime; 176 | mRTCPThread.sendReciveReport(RTCPPackage.RTCP_PAYLOAD_TYPE_RR); 177 | } 178 | // RTCP END 179 | } catch (IOException e) { 180 | //e.printStackTrace(); 181 | Log.e(TAG, "RTP Socket maybe exited !"); 182 | } 183 | } 184 | Log.d(TAG, "RTPThread over !"); 185 | } 186 | 187 | H264Package mH264Package = new H264Package(); 188 | NALUnit mNALUnit = new NALUnit(); 189 | private long test = 8; 190 | private void recoverH264Package(RTPPackage rtp) throws IOException { 191 | if(mDbgIsComplete) { 192 | mFirstPackageTimestamp = System.currentTimeMillis(); 193 | mDbgIsComplete = false; 194 | } 195 | NALUnit nalu = mNALUnit.getCompleteNALUnit(rtp); 196 | // NALUnit tmpHeader = NALUnit.parseHeader(rtp); //没用到? 197 | /*Log.d(DefaultConfig.TAG, ">. get one NALU. type="+tmpHeader.nal_unit_type+";mb="+tmpHeader.first_mb_in_slice 198 | +";fu_type="+tmpHeader.fua_nal_unit_type+";fu_mb="+tmpHeader.fua_first_mb_in_slice 199 | +";fu_start?"+tmpHeader.fu_isStart+";fu_end?"+tmpHeader.fu_isEnd);*/ 200 | 201 | if (nalu != null) { 202 | if(DEBUG_NALU) Log.d(TAG, "** get complete NALU frame ! **"); 203 | H264Package h264frame = null; 204 | mH264Package.sendNALUnit(nalu); 205 | while((h264frame = mH264Package.getCompleteH264()) != null) { 206 | if (h264frame != null) { 207 | long currentTime = System.currentTimeMillis(); 208 | mH264FrameInterval = Math.max(currentTime - mLast264Timestamp, mH264FrameInterval); 209 | mLast264Timestamp = currentTime; 210 | mH264FrameDelay = Math.max(currentTime - mFirstPackageTimestamp, mH264FrameDelay); 211 | mDbgIsComplete = true; 212 | mNaluTypeCount[h264frame.mNALU.get(0).nal_unit_type] += 1; 213 | //Log.d(TAG, "** get complete h264 frame ! **"); 214 | if (mOnFrameCallback != null) { 215 | // if (DefaultConfig.DEFAULT_DEBUG_DELAY) 216 | // Log.e(DefaultConfig.TAG, "2. get a complete h264 frame:[" + h264frame.mNALU.get(0).nal_unit_type + "]: " + System.currentTimeMillis()); 217 | mOnFrameCallback.OnFrame(h264frame.getBytes()); 218 | //else Log.e(DefaultConfig.TAG, "*. skip"); 219 | } 220 | } else { 221 | //Log.d(TAG, "** combining h264 frame ... **"); 222 | } 223 | } 224 | } else { 225 | //Log.d(TAG, "** combining NALU frame ... **"); 226 | } 227 | } 228 | 229 | AACPackage aac =new AACPackage(); 230 | private void recoverAACPackage(RTPPackage rtp) throws IOException { 231 | aac = aac.getCompleteAACPackage(rtp); 232 | if (mOnFrameCallback != null) 233 | mOnFrameCallback.OnFrame(new AACPackage().getCompleteAACPackage(rtp).data); 234 | } 235 | } 236 | 237 | private class RTCPThread extends Thread { 238 | private HandlerThread mSenderThread; 239 | private Handler mSenderHandler; 240 | private DatagramSocket mSocket; 241 | private DatagramPacket mPacket; 242 | private volatile boolean mIsStop = false; 243 | 244 | private int mLocalPort = 0; 245 | private int mRemotePort = 0; 246 | private String mSerIP = ""; 247 | 248 | public RTCPThread(int localPort, String serverip, int serverPort) { 249 | mLocalPort = localPort; 250 | mRemotePort = serverPort; 251 | mSerIP = serverip; 252 | 253 | mSenderThread = new HandlerThread("RTCHSender"); 254 | mSenderThread.start(); 255 | mSenderHandler = new Handler(mSenderThread.getLooper()); 256 | try { 257 | mSocket = new DatagramSocket(mLocalPort); 258 | } catch (SocketException e) { 259 | e.printStackTrace(); 260 | } 261 | mPacket = new DatagramPacket(message,message.length); 262 | } 263 | 264 | public void exit(){ 265 | Log.w(TAG, "RTCP thread exiting ..."); 266 | mIsStop = true; 267 | } 268 | 269 | public void disconnect() { 270 | mSocket.disconnect(); 271 | mSocket.close(); 272 | } 273 | RTCPPackage mRTCPPackage = new RTCPPackage(); 274 | @Override 275 | public void run() { 276 | while(!mIsStop) { 277 | try { 278 | if (mSocket.isClosed()) 279 | return; 280 | mSocket.receive(mPacket); 281 | RTCPPackage sr = mRTCPPackage.parseSR(mPacket.getData()); 282 | //byte[] sr = mPacket.getData(); 283 | mLastSR = sr.mNtpTimestamp; 284 | mLastTimestamp = (short) System.currentTimeMillis(); 285 | Log.d(TAG, "RTCP recieve a SR package("+mPacket.getData().length+"): ntp="+sr.mNtpTimestamp); 286 | } catch (IOException e) { 287 | //e.printStackTrace(); 288 | Log.e(TAG, "RTCP Socket maybe exited !"); 289 | } 290 | } 291 | Log.d(TAG, "RTCPThread over !"); 292 | } 293 | 294 | public void sendReciveReport(final byte ptype) { 295 | mSenderHandler.post(new Runnable() { 296 | @Override 297 | public void run() { 298 | Log.d(TAG, "Rtcp send report :"+ptype); 299 | sendReport(ptype); 300 | } 301 | }); 302 | } 303 | 304 | private void sendReport(final byte ptype) { 305 | if (mServerSSRC == -1) 306 | return; 307 | 308 | long dlsr = (System.currentTimeMillis() - mLastTimestamp)/65536000; 309 | RTCPPackage packet = mRTCPPackage.create(ptype, mServerSSRC, (int)mLastSequenceNum, mLastSR, dlsr); 310 | try { 311 | if (ptype == RTCPPackage.RTCP_PAYLOAD_TYPE_RR) { 312 | byte[] rtcpRR = packet.getRRBytes(); 313 | Log.d(TAG, "send RR, len:" + packet.getRRBytes().length + "| ssrc:" + mServerSSRC); 314 | mPacket = new DatagramPacket(rtcpRR, rtcpRR.length, InetAddress.getByName(mSerIP), mRemotePort); 315 | } else if (ptype == RTCPPackage.RTCP_PAYLOAD_TYPE_BYE) { 316 | byte[] rtcpBYE = packet.getBYEBytes(); 317 | Log.d(TAG, "send BYE, len:" + packet.getBYEBytes().length + "| ssrc:" + mServerSSRC); 318 | mPacket = new DatagramPacket(rtcpBYE, rtcpBYE.length, InetAddress.getByName(mSerIP), mRemotePort); 319 | } 320 | 321 | mSocket.send(mPacket); 322 | 323 | if (ptype == RTCPPackage.RTCP_PAYLOAD_TYPE_BYE) { 324 | mRTCPThread.exit(); 325 | mRTPThread.exit(); 326 | mRTCPThread.disconnect(); 327 | mRTPThread.disconnect(); 328 | } 329 | } catch (UnknownHostException e) { 330 | e.printStackTrace(); 331 | } catch (IOException e) { 332 | e.printStackTrace(); 333 | } 334 | } 335 | 336 | } 337 | 338 | public interface OnFrameListener { 339 | void OnFrame(byte[] buffer); 340 | } 341 | public void setOnFrameListener(OnFrameListener listener) { 342 | mOnFrameCallback = listener; 343 | } 344 | } 345 | -------------------------------------------------------------------------------- /app/src/main/java/com/rockchip/rkmediacodecdemo/rtsp/RTPPackage.java: -------------------------------------------------------------------------------- 1 | package com.rockchip.rkmediacodecdemo.rtsp; 2 | 3 | /** 4 | * Created by zhanghao on 2016/12/30. 5 | */ 6 | 7 | public class RTPPackage { 8 | private static final String TAG = "RTPPackage"; 9 | 10 | public static final int PTYPE_DYNAMIC_MIN = 96; 11 | public static final int PTYPE_DYNAMIC_MAX = 127; 12 | 13 | public byte ver; 14 | public byte pillow; 15 | public byte extend; 16 | public byte csrcCnt; 17 | public byte mark; 18 | public byte ptype; 19 | public int sequenceNumber; 20 | public long timestamp; 21 | public int ssrc; 22 | public byte[] csrc; 23 | /* 24 | ver 版本号(V):2比特,用来标志使用的RTP版本。 25 | pillow 填充位(P):1比特,如果该位置位,则该RTP包的尾部就包含附加的填充字节。 26 | extend 扩展位(X):1比特,如果该位置位的话,RTP固定头部后面就跟有一个扩展头部。 27 | csrcCnt CSRC计数器(CC):4比特,含有固定头部后面跟着的CSRC的数目。 28 | mark 标记位(M):1比特,该位的解释由配置文档(Profile)来承担. 29 | ptype 载荷类型(PT):7比特,标识了RTP载荷的类型。 30 | sequenceNumber 序列号(SN):16比特,发送方在每发送完一个RTP包后就将该域的值增加1, 31 | 接收方可以由该域检测包的丢失及恢复包序列。序列号的初始值是随机的。 32 | timestamp 时间戳:32比特,记录了该包中数据的第一个字节的采样时刻。在一次会话开始时,时间戳初始化成一个初始值。 33 | 即使在没有信号发送时,时间戳的数值也要随时间而不断地增加(时间在流逝嘛)。x` 34 | 时间戳是去除抖动和实现同步不可缺少的。 35 | ssrc 同步源标识符(SSRC):32比特,同步源就是指RTP包流的来源。在同一个RTP会话中不能有两个相同的SSRC值。 36 | 该标识符是随机选取的 RFC1889推荐了MD5随机算法。 37 | csrc 贡献源列表(CSRC List):0~15项,每项32比特,用来标志对一个RTP混合器产生的新包有贡献的所有RTP包的源。 38 | 由混合器将这些有贡献的SSRC标识符插入表中。SSRC标识符都被列出来,以便接收端能正确指出交谈双方的身份。 39 | */ 40 | 41 | /* 42 | 0 1 2 3 43 | 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 0 1 2 3 4 5 6 7 44 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 45 | |V=2|P|X| CC |M| PT | sequence number | 46 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 47 | | timestamp | 48 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 49 | | synchronization source (SSRC) identifier | 50 | +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ 51 | | contributing source (CSRC) identifiers | 52 | | .... | 53 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ 54 | */ 55 | public RTPPackage create(byte[] buf, int size){ 56 | RTPPackage rtpPack = new RTPPackage(); 57 | // byte 0 58 | rtpPack.ver = (byte)((buf[0]&0xC0) >> 6); 59 | rtpPack.pillow = (byte)((buf[0]&0x20) >> 5); 60 | rtpPack.extend = (byte)((buf[0]&0x10) >> 4); 61 | rtpPack.csrcCnt = (byte)(buf[0]&0xF); 62 | 63 | // byte 1 64 | rtpPack.mark = (byte)((buf[1]&0x80) >> 7); 65 | rtpPack.ptype = (byte)(buf[1]&0x7F); 66 | 67 | // byte 2-3 68 | rtpPack.sequenceNumber = (((buf[2] & 0xFF) << 8) | (buf[3] & 0xFF)); 69 | 70 | // byte 4 71 | rtpPack.timestamp = ((long)(buf[4] & 0xFF) << 24) | ((long)(buf[5]&0xFF) << 16) | ((long)(buf[6]&0xFF) << 8) | (long)(buf[7]&0xFF); 72 | 73 | // byte 5 74 | rtpPack.ssrc = ((buf[8]&0xFF) << 24) | ((buf[9]&0xFF) << 16) | ((buf[10]&0xFF) << 8) | (buf[11]&0xFF); 75 | 76 | // byte 6 77 | rtpPack.csrc = new byte[size-12]; 78 | System.arraycopy(buf, 12, rtpPack.csrc, 0, size - 12); 79 | 80 | return rtpPack; 81 | } 82 | 83 | public String toString(){ 84 | return "[V="+ver+" csrcCnt="+csrcCnt + " ptype="+ ptype +" sequence num="+sequenceNumber + " timestamp=" + timestamp + "]"; 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /app/src/main/java/com/rockchip/rkmediacodecdemo/rtsp/RTSPConnector.java: -------------------------------------------------------------------------------- 1 | package com.rockchip.rkmediacodecdemo.rtsp; 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.io.InputStream; 9 | import java.io.OutputStream; 10 | import java.net.Socket; 11 | import java.util.HashMap; 12 | import java.util.Locale; 13 | import java.util.regex.Matcher; 14 | import java.util.regex.Pattern; 15 | 16 | /** 17 | * Created by zhanghao on 2016/12/28. 18 | */ 19 | 20 | public class RTSPConnector { 21 | private static final String TAG = "RTSPConnector"; 22 | private Socket mSocket = null; 23 | private InputStream mInputStream = null; 24 | private OutputStream mOutputStream = null; 25 | private int mCSeq = 1; 26 | private String mIP = null; 27 | private int mPort = 554; 28 | private String mFunc = null; 29 | private boolean mIsTCP = false; 30 | 31 | private HandlerThread mResponseThread = null; 32 | private Handler mResponseHandler = null; 33 | 34 | private RTPConnector mRTPConnect = null; 35 | private HashMap mHeaders = null; 36 | private String mSession = null; 37 | private boolean mHasAudioTrack = false; 38 | private boolean mHasVideoTrack = false; 39 | private boolean mDidSessionSet = false; 40 | private boolean mDidMediaScriptSet = false; 41 | 42 | private int mRTPClientPort = 0; 43 | private int mRTPServerPort = 0; 44 | 45 | 46 | private RTPConnector.OnFrameListener mOnFrameCallback = null; 47 | 48 | private final Pattern regexStatus = Pattern.compile("RTSP/\\d.\\d (\\d+) .+", Pattern.CASE_INSENSITIVE); 49 | private final Pattern regexHeader = Pattern.compile("(\\S+): (.+)", Pattern.CASE_INSENSITIVE); 50 | private final Pattern regexUDPTransport = Pattern.compile("client_port=(\\d+)-\\d+;server_port=(\\d+)-\\d+", Pattern.CASE_INSENSITIVE); 51 | private final Pattern regexTCPTransport = Pattern.compile("client_port=(\\d+)-\\d+;", Pattern.CASE_INSENSITIVE); 52 | private final Pattern regexSessionWithTimeout = Pattern.compile("(\\S+);timeout=(\\d+)", Pattern.CASE_INSENSITIVE); 53 | // private final Pattern regexSDPgetTrack1 = Pattern.compile("trackID=(\\d+)",Pattern.CASE_INSENSITIVE); 54 | // private final Pattern regexSDPgetTrack2 = Pattern.compile("control:(\\S+)",Pattern.CASE_INSENSITIVE); 55 | private final Pattern regexSDP_MediadeScript = Pattern.compile("m=(\\S+) .+", Pattern.CASE_INSENSITIVE); 56 | private final Pattern regexSDP_PacketizationMode = Pattern.compile("packetization-mode=(\\d);", Pattern.CASE_INSENSITIVE); 57 | private final Pattern regexSDP_SPS_PPS = Pattern.compile("sprop-parameter-sets=(\\S+),(\\S+)", Pattern.CASE_INSENSITIVE); 58 | private final Pattern regexSDP_Length = Pattern.compile("Content-length: (\\d+)", Pattern.CASE_INSENSITIVE); 59 | // private static final Pattern regexSDPstartFlag = Pattern.compile("v=(\\d)",Pattern.CASE_INSENSITIVE); 60 | 61 | public RTSPConnector(String IP, int port, String func) throws IOException { 62 | mIP = IP; 63 | mPort= port; 64 | mFunc = func; 65 | 66 | mSocket = new Socket(IP, port); 67 | mInputStream = mSocket.getInputStream(); 68 | mOutputStream = mSocket.getOutputStream(); 69 | 70 | mResponseThread = new HandlerThread(TAG+"Rep"); 71 | mResponseThread.start(); 72 | mResponseHandler = new Handler(mResponseThread.getLooper()); 73 | 74 | mHeaders = new HashMap<>(); 75 | } 76 | 77 | public byte[] recvMsg(InputStream inpustream) { 78 | try { 79 | byte len[] = new byte[2048]; 80 | int count = inpustream.read(len); 81 | if(count<=0) 82 | return null; 83 | byte[] temp = new byte[count]; 84 | for (int i = 0; i < count; i++) { 85 | temp[i] = len[i]; 86 | } 87 | return temp; 88 | } catch (IOException e) { 89 | e.printStackTrace(); 90 | } 91 | return null; 92 | } 93 | private String getHeaders() { 94 | return "CSeq: " + (++mCSeq) + "\r\n" 95 | + "User-Agent: RockchipRtspClient("+"1.0"+") \r\n" 96 | + ((mSession == null)?"":("Session: " + mSession + "\r\n")); 97 | //+ "\r\n"; 98 | } 99 | 100 | public void requestOptions() throws IOException { 101 | String request = "OPTIONS rtsp://" + mIP + ":" + mPort + "/" + mFunc + " RTSP/1.0\r\n" + getHeaders() + "\r\n"; 102 | Log.d(TAG, ">> " + request); 103 | mOutputStream.write(request.getBytes("UTF-8")); 104 | mOutputStream.flush(); 105 | parseResponse(); 106 | 107 | 108 | } 109 | 110 | public void requestDescribe() throws IOException { 111 | String request = "DESCRIBE rtsp://" + mIP + ":" + mPort + "/" + mFunc + " RTSP/1.0\r\n" + getHeaders() 112 | + "Accept: application/sdp\r\n" + "\r\n"; 113 | Log.d(TAG, ">> " + request); 114 | mOutputStream.write(request.getBytes("UTF-8")); 115 | mOutputStream.flush(); 116 | parseResponse(); 117 | } 118 | 119 | public void requestSetup(String track, int clientport) throws IOException { 120 | Matcher matcher; 121 | String request = "SETUP rtsp://" + mIP + "/" + mFunc + "/" + track +" RTSP/1.0\r\n" 122 | + getHeaders() 123 | + "Transport: RTP/AVP/"+ (mIsTCP?"TCP":"UDP") + ";unicast;client_port="+clientport+"-"+(clientport+1) + "\r\n" 124 | + "\r\n"; 125 | Log.d(TAG, ">> " + request); 126 | mOutputStream.write(request.getBytes("UTF-8")); 127 | mOutputStream.flush(); 128 | 129 | //wait(mDidSessionSet,"mDidSessionSet"); 130 | while(!mDidSessionSet){ 131 | try { 132 | parseResponse(); 133 | Thread.sleep(10); 134 | //Log.d(TAG, "wait mDidSessionSet:" + mDidSessionSet); 135 | } catch (InterruptedException e) { 136 | e.printStackTrace(); 137 | } 138 | } 139 | 140 | matcher = regexSessionWithTimeout.matcher(mHeaders.get("session")); 141 | if(matcher.find()) { 142 | mSession = matcher.group(1); 143 | } 144 | else { 145 | mSession = mHeaders.get("session"); 146 | } 147 | Log.d(TAG, "the session is " + mSession); 148 | 149 | Log.d(TAG, "requestSetup: mHeaders" + mHeaders.toString()); 150 | try { 151 | if(mIsTCP) matcher = regexTCPTransport.matcher(mHeaders.get("transport")); 152 | else matcher = regexUDPTransport.matcher(mHeaders.get("transport")); 153 | }catch (NullPointerException e){ 154 | e.printStackTrace(); 155 | } 156 | if(matcher.find()) { 157 | Log.d(TAG, "The client port is:" + matcher.group(1) + " ,the server prot is:" + (mIsTCP?"null":matcher.group(2)) + "..."); 158 | mRTPClientPort = Integer.parseInt(matcher.group(1)); 159 | if(!mIsTCP) mRTPServerPort = Integer.parseInt(matcher.group(2)); 160 | //prepare for the decoder 161 | //wait(mDidMediaScriptSet,"mDidMediaScriptSet"); 162 | while(!mDidMediaScriptSet){ 163 | try { 164 | Thread.sleep(10); 165 | //Log.d(TAG, "wait mDidMediaScriptSet:" + mDidMediaScriptSet); 166 | } catch (InterruptedException e) { 167 | e.printStackTrace(); 168 | } 169 | } 170 | RTPConnector.RTPType rtpType = null; 171 | Log.d(TAG, "requestSetup: mHasVideoTrack = "+mHasVideoTrack); 172 | Log.d(TAG, "requestSetup: mHasAudioTrack = "+mHasAudioTrack); 173 | if (mHasVideoTrack ) rtpType = RTPConnector.RTPType.VIDEO; 174 | else if(mHasAudioTrack ) rtpType = RTPConnector.RTPType.AUDIO; 175 | else Log.e(TAG, "unsupport multi track !"); 176 | Log.d(TAG, "Create Socket from client :"+mRTPClientPort+" to server:"+mIP+":"+mRTPServerPort); 177 | mRTPConnect = new RTPConnector(mIsTCP, mRTPClientPort, mIP, mRTPServerPort, rtpType); 178 | mRTPConnect.setOnFrameListener(mOnFrameCallback); 179 | mRTPConnect.connect(); 180 | } else { 181 | Log.e(TAG, "transport setting no found !"); 182 | } 183 | } 184 | 185 | public void requestPlay() throws IOException { 186 | String request = "PLAY rtsp://" + mIP + ":" + mPort + "/" + mFunc + "/ RTSP/1.0\r\n" 187 | + getHeaders() 188 | + "Range: npt=0.000-\r\n" 189 | + "\r\n"; 190 | Log.d(TAG, ">> " + request); 191 | mOutputStream.write(request.getBytes("UTF-8")); 192 | mOutputStream.flush(); 193 | parseResponse(); 194 | } 195 | 196 | public void requestGetParameter() throws IOException { 197 | String request = "GET_PARAMETER rtsp://" + mIP + ":" + mPort + "/" + mFunc + "/ RTSP/1.0\r\n" 198 | + getHeaders() + "\r\n"; 199 | Log.d(TAG, ">> " + request); 200 | mOutputStream.write(request.getBytes("UTF-8")); 201 | mOutputStream.flush(); 202 | parseResponse(); 203 | } 204 | 205 | public void requestTeardown(String track) throws IOException { 206 | String request = "TEARDOWN rtsp://" + mIP + "/" + mFunc + "/" + track + " RTSP/1.0\r\n" + getHeaders() + "\r\n"; 207 | Log.d(TAG, ">> " + request); 208 | mOutputStream.write(request.getBytes("UTF-8")); 209 | mOutputStream.flush(); 210 | } 211 | 212 | public void parseResponse(/*InputStream input*/) throws IOException { 213 | // BufferedReader bufferReader = new BufferedReader(new InputStreamReader(input)); 214 | 215 | String line; 216 | Matcher matcher; 217 | int state = -1; 218 | 219 | int sdpContentLength = 0; 220 | int packetizationMode = 0; 221 | String SPS = ""; 222 | String PPS = ""; 223 | 224 | // 接受服务器的信息 225 | // while (true) { 226 | byte[] by = recvMsg(mInputStream); 227 | if (by == null) { 228 | return; 229 | } 230 | 231 | try { 232 | line = new String(by); 233 | 234 | String[] sArray = line.split("\r\n"); 235 | for (int i = 0; i < sArray.length; i++) { 236 | 237 | Log.d(TAG, "-----------------------i" + i + ":" + sArray[i].toString()); 238 | // while ( (file_dialog_item = bufferReader.readLine()) != null) { 239 | Log.d(TAG, "<< " + sArray[i]); 240 | 241 | /* GET STATUS */ 242 | matcher = regexStatus.matcher(sArray[i]); 243 | if (matcher.find()) { 244 | state = Integer.parseInt(matcher.group(1)); 245 | // Log.d(TAG, "++ [STATUS = "+state+"]"); 246 | } 247 | 248 | /* GET HEADER */ 249 | matcher = regexHeader.matcher(sArray[i]); 250 | if (matcher.find()) { 251 | String key = matcher.group(1).toLowerCase(Locale.US); 252 | String value = matcher.group(2); 253 | mHeaders.put(key, value); 254 | // Log.d(TAG, "++ [HEADER] "+ key + " : " + value); 255 | 256 | if (key.equals("session")) { 257 | mDidSessionSet = true; 258 | Log.d(TAG, "session set !" + mDidSessionSet); 259 | } 260 | } 261 | 262 | /* GET SDP length */ 263 | matcher = regexSDP_Length.matcher(sArray[i]); 264 | if (matcher.find()) { 265 | sdpContentLength = Integer.parseInt(matcher.group(1)); 266 | // Log.d(TAG, "++ [SDP LENGTH]"); 267 | } 268 | 269 | /* GET SDP MediaScript */ 270 | matcher = regexSDP_MediadeScript.matcher(sArray[i]); 271 | if (matcher.find()) { 272 | if (matcher.group(1).equalsIgnoreCase("audio")) { 273 | mHasAudioTrack = true; 274 | mDidMediaScriptSet = true; 275 | } else if (matcher.group(1).equalsIgnoreCase("video")) { 276 | mHasVideoTrack = true; 277 | mDidMediaScriptSet = true; 278 | } 279 | // Log.d(TAG, "++ [SDP MEDIASCRIPT]" + matcher.group(1)); 280 | } 281 | 282 | mHasVideoTrack = true; 283 | /* GET SDP Packetization Mode */ 284 | matcher = regexSDP_PacketizationMode.matcher(sArray[i]); 285 | if (matcher.find()) { 286 | packetizationMode = Integer.parseInt(matcher.group(1)); 287 | // Log.d(TAG, "++ [SDP PacketizationMode]" + packetizationMode); 288 | } 289 | 290 | /* GET SDP SPS PPS */ 291 | matcher = regexSDP_SPS_PPS.matcher(sArray[i]); 292 | if (matcher.find()) { 293 | SPS = matcher.group(1); 294 | PPS = matcher.group(2); 295 | // Log.d(TAG, "++ [SDP SPS PPS]"); 296 | } 297 | } 298 | // break; 299 | }catch (Exception e){ 300 | e.printStackTrace(); 301 | Log.d(TAG, "run Thread::解析Socket数据异常!!!!!!!!!!!!!!!" ); 302 | } 303 | 304 | // } 305 | 306 | Log.w(TAG, "== Connection lost"); 307 | } 308 | 309 | public void disconnect() { 310 | if(mRTPConnect != null) 311 | mRTPConnect.disconnect(); 312 | } 313 | 314 | /*private void wait(Boolean sth, String debug){ 315 | while(!sth){ 316 | try { 317 | Thread.sleep(1000); 318 | Log.d(TAG, "wait .." + debug + ":" + sth); 319 | } catch (InterruptedException e) { 320 | e.printStackTrace(); 321 | } 322 | } 323 | }*/ 324 | 325 | public void setOnFrameListener(RTPConnector.OnFrameListener listener) { 326 | mOnFrameCallback = listener; 327 | } 328 | 329 | public int getLostPackageCount(){ 330 | if (mRTPConnect != null) 331 | return mRTPConnect.getLostPackageCount(); 332 | else 333 | return 0; 334 | } 335 | public int getRecvPackageCount(){ 336 | if (mRTPConnect != null) 337 | return mRTPConnect.getRecvPackageCount(); 338 | else 339 | return 0; 340 | } 341 | public long getLastestTimestamp(){ 342 | if (mRTPConnect != null) 343 | return mRTPConnect.getLastestSequeceTime(); 344 | else 345 | return 0; 346 | } 347 | public void resetPackageCount(){ 348 | if (mRTPConnect != null) 349 | mRTPConnect.resetPackageCount(); 350 | } 351 | 352 | public long getH264FrameDelay(){ 353 | if (mRTPConnect != null) 354 | return mRTPConnect.getH264FrameDelay(); 355 | else 356 | return 0; 357 | } 358 | public long getH264FrameInterval(){ 359 | if (mRTPConnect != null) 360 | return mRTPConnect.getH264FrameInterval(); 361 | else 362 | return 0; 363 | } 364 | public long getH264Bps() { 365 | if (mRTPConnect != null) 366 | return mRTPConnect.getH264Bps(); 367 | else 368 | return 0; 369 | } 370 | public int[] getH264NaluTypeCount(){ 371 | if (mRTPConnect != null) 372 | return mRTPConnect.getH264NaluTypeCount(); 373 | else 374 | return new int[10]; 375 | } 376 | } 377 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 12 | 13 | 19 | 22 | 25 | 26 | 27 | 28 | 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 12 | 13 | 19 | 20 | 26 | 27 | 31 | 32 | 33 | 38 | 39 | 43 | 44 | 45 | 46 | 52 | 53 | 59 | 60 | 64 | 65 | 66 | 71 | 72 | 76 | 77 | 78 | 79 |