├── app
├── .gitignore
├── libs
│ └── mina-core-2.0.7.jar
├── src
│ └── main
│ │ ├── res
│ │ ├── drawable-hdpi
│ │ │ ├── ic_exit.png
│ │ │ └── ic_lights.png
│ │ ├── drawable-mdpi
│ │ │ ├── ic_exit.png
│ │ │ └── ic_lights.png
│ │ ├── drawable-xhdpi
│ │ │ ├── ic_exit.png
│ │ │ └── ic_lights.png
│ │ ├── mipmap-hdpi
│ │ │ ├── ic_onvif.png
│ │ │ ├── ic_launcher.png
│ │ │ ├── ic_arrow_up_trans.png
│ │ │ ├── ic_launcher_round.png
│ │ │ ├── ic_arrow_down_trans.png
│ │ │ ├── ic_arrow_left_trans.png
│ │ │ └── ic_arrow_right_trans.png
│ │ ├── mipmap-mdpi
│ │ │ ├── ic_onvif.png
│ │ │ ├── ic_launcher.png
│ │ │ ├── ic_arrow_up_trans.png
│ │ │ ├── ic_launcher_round.png
│ │ │ ├── ic_arrow_down_trans.png
│ │ │ ├── ic_arrow_left_trans.png
│ │ │ └── ic_arrow_right_trans.png
│ │ ├── mipmap-xhdpi
│ │ │ ├── ic_onvif.png
│ │ │ ├── ic_launcher.png
│ │ │ ├── ic_arrow_down_trans.png
│ │ │ ├── ic_arrow_left_trans.png
│ │ │ ├── ic_arrow_up_trans.png
│ │ │ ├── ic_launcher_round.png
│ │ │ └── ic_arrow_right_trans.png
│ │ ├── mipmap-xxhdpi
│ │ │ ├── ic_onvif.png
│ │ │ ├── ic_launcher.png
│ │ │ ├── ic_arrow_up_trans.png
│ │ │ ├── ic_launcher_round.png
│ │ │ ├── ic_arrow_down_trans.png
│ │ │ ├── ic_arrow_left_trans.png
│ │ │ └── ic_arrow_right_trans.png
│ │ ├── drawable-xxhdpi
│ │ │ ├── ic_exit.png
│ │ │ └── ic_lights.png
│ │ ├── mipmap-xxxhdpi
│ │ │ ├── ic_onvif.png
│ │ │ ├── ic_launcher.png
│ │ │ ├── ic_arrow_up_trans.png
│ │ │ ├── ic_launcher_round.png
│ │ │ ├── ic_arrow_down_trans.png
│ │ │ ├── ic_arrow_left_trans.png
│ │ │ └── ic_arrow_right_trans.png
│ │ ├── values
│ │ │ ├── colors.xml
│ │ │ ├── styles.xml
│ │ │ └── strings.xml
│ │ ├── layout
│ │ │ ├── my_menu_item.xml
│ │ │ ├── onvif_discovery.xml
│ │ │ ├── cctv_view.xml
│ │ │ └── onvif_settings.xml
│ │ ├── menu
│ │ │ ├── onvif_settings_menu.xml
│ │ │ └── onvif_menu.xml
│ │ └── layout-land
│ │ │ └── cctv_view.xml
│ │ ├── java
│ │ └── tk
│ │ │ └── giesecke
│ │ │ └── cctvview
│ │ │ ├── Rtsp
│ │ │ ├── Audio
│ │ │ │ ├── ACCStream.java
│ │ │ │ └── AudioStream.java
│ │ │ ├── Socket
│ │ │ │ ├── RtcpSocket.java
│ │ │ │ └── RtpSocket.java
│ │ │ ├── Stream
│ │ │ │ └── RtpStream.java
│ │ │ ├── Video
│ │ │ │ ├── VideoStream.java
│ │ │ │ └── H264Stream.java
│ │ │ └── RtspClient.java
│ │ │ ├── Onvif
│ │ │ ├── OnvifDeviceDNS.java
│ │ │ ├── OnvifPtzNodes.java
│ │ │ ├── OnvifPtzNode.java
│ │ │ ├── OnvifPtzConfigurations.java
│ │ │ ├── OnvifDeviceNetworkProtocols.java
│ │ │ ├── OnvifDeviceNetworkDefaultGateway.java
│ │ │ ├── OnvifMediaOSDs.java
│ │ │ ├── OnvifPtzConfiguration.java
│ │ │ ├── OnvifPtzStop.java
│ │ │ ├── OnvifPtzAbsoluteMove.java
│ │ │ ├── OnvifPtzContinuousMove.java
│ │ │ ├── OnvifDevice.java
│ │ │ ├── OnvifMediaStreamUri.java
│ │ │ ├── OnvifDeviceInformation.java
│ │ │ ├── OnvifDeviceNetworkInterfaces.java
│ │ │ ├── OnvifDeviceScopes.java
│ │ │ ├── OnvifResponseParser.java
│ │ │ ├── OnvifHeaderBody.java
│ │ │ ├── DeviceDiscovery.java
│ │ │ ├── OnvifMediaProfiles.java
│ │ │ └── OnvifDeviceCapabilities.java
│ │ │ ├── BootReceiver.java
│ │ │ ├── findSPMbyMDNS.java
│ │ │ ├── OnvifSettings.java
│ │ │ └── OnvifDiscovery.java
│ │ └── AndroidManifest.xml
├── lint.xml
├── proguard-rules.pro
└── build.gradle
├── settings.gradle
├── .idea
├── copyright
│ └── profiles_settings.xml
├── encodings.xml
├── vcs.xml
├── inspectionProfiles
│ ├── profiles_settings.xml
│ └── Project_Default.xml
├── modules.xml
├── runConfigurations.xml
├── gradle.xml
├── compiler.xml
└── misc.xml
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── .gitignore
├── gradle.properties
├── README.md
├── gradlew.bat
└── gradlew
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------
/app/libs/mina-core-2.0.7.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/beegee-tokyo/CCTVview/HEAD/app/libs/mina-core-2.0.7.jar
--------------------------------------------------------------------------------
/.idea/copyright/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/beegee-tokyo/CCTVview/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/ic_exit.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/beegee-tokyo/CCTVview/HEAD/app/src/main/res/drawable-hdpi/ic_exit.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/ic_exit.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/beegee-tokyo/CCTVview/HEAD/app/src/main/res/drawable-mdpi/ic_exit.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/ic_exit.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/beegee-tokyo/CCTVview/HEAD/app/src/main/res/drawable-xhdpi/ic_exit.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_onvif.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/beegee-tokyo/CCTVview/HEAD/app/src/main/res/mipmap-hdpi/ic_onvif.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_onvif.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/beegee-tokyo/CCTVview/HEAD/app/src/main/res/mipmap-mdpi/ic_onvif.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_onvif.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/beegee-tokyo/CCTVview/HEAD/app/src/main/res/mipmap-xhdpi/ic_onvif.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_onvif.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/beegee-tokyo/CCTVview/HEAD/app/src/main/res/mipmap-xxhdpi/ic_onvif.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/ic_lights.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/beegee-tokyo/CCTVview/HEAD/app/src/main/res/drawable-hdpi/ic_lights.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/ic_lights.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/beegee-tokyo/CCTVview/HEAD/app/src/main/res/drawable-mdpi/ic_lights.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/ic_lights.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/beegee-tokyo/CCTVview/HEAD/app/src/main/res/drawable-xhdpi/ic_lights.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ic_exit.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/beegee-tokyo/CCTVview/HEAD/app/src/main/res/drawable-xxhdpi/ic_exit.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/beegee-tokyo/CCTVview/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/beegee-tokyo/CCTVview/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/beegee-tokyo/CCTVview/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_onvif.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/beegee-tokyo/CCTVview/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_onvif.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ic_lights.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/beegee-tokyo/CCTVview/HEAD/app/src/main/res/drawable-xxhdpi/ic_lights.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/beegee-tokyo/CCTVview/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/beegee-tokyo/CCTVview/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_arrow_up_trans.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/beegee-tokyo/CCTVview/HEAD/app/src/main/res/mipmap-hdpi/ic_arrow_up_trans.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/beegee-tokyo/CCTVview/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_arrow_up_trans.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/beegee-tokyo/CCTVview/HEAD/app/src/main/res/mipmap-mdpi/ic_arrow_up_trans.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/beegee-tokyo/CCTVview/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_arrow_down_trans.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/beegee-tokyo/CCTVview/HEAD/app/src/main/res/mipmap-hdpi/ic_arrow_down_trans.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_arrow_left_trans.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/beegee-tokyo/CCTVview/HEAD/app/src/main/res/mipmap-hdpi/ic_arrow_left_trans.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_arrow_right_trans.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/beegee-tokyo/CCTVview/HEAD/app/src/main/res/mipmap-hdpi/ic_arrow_right_trans.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_arrow_down_trans.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/beegee-tokyo/CCTVview/HEAD/app/src/main/res/mipmap-mdpi/ic_arrow_down_trans.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_arrow_left_trans.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/beegee-tokyo/CCTVview/HEAD/app/src/main/res/mipmap-mdpi/ic_arrow_left_trans.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_arrow_right_trans.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/beegee-tokyo/CCTVview/HEAD/app/src/main/res/mipmap-mdpi/ic_arrow_right_trans.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_arrow_down_trans.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/beegee-tokyo/CCTVview/HEAD/app/src/main/res/mipmap-xhdpi/ic_arrow_down_trans.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_arrow_left_trans.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/beegee-tokyo/CCTVview/HEAD/app/src/main/res/mipmap-xhdpi/ic_arrow_left_trans.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_arrow_up_trans.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/beegee-tokyo/CCTVview/HEAD/app/src/main/res/mipmap-xhdpi/ic_arrow_up_trans.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/beegee-tokyo/CCTVview/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_arrow_up_trans.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/beegee-tokyo/CCTVview/HEAD/app/src/main/res/mipmap-xxhdpi/ic_arrow_up_trans.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/beegee-tokyo/CCTVview/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_arrow_up_trans.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/beegee-tokyo/CCTVview/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_arrow_up_trans.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/beegee-tokyo/CCTVview/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_arrow_right_trans.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/beegee-tokyo/CCTVview/HEAD/app/src/main/res/mipmap-xhdpi/ic_arrow_right_trans.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_arrow_down_trans.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/beegee-tokyo/CCTVview/HEAD/app/src/main/res/mipmap-xxhdpi/ic_arrow_down_trans.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_arrow_left_trans.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/beegee-tokyo/CCTVview/HEAD/app/src/main/res/mipmap-xxhdpi/ic_arrow_left_trans.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_arrow_right_trans.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/beegee-tokyo/CCTVview/HEAD/app/src/main/res/mipmap-xxhdpi/ic_arrow_right_trans.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_arrow_down_trans.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/beegee-tokyo/CCTVview/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_arrow_down_trans.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_arrow_left_trans.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/beegee-tokyo/CCTVview/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_arrow_left_trans.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_arrow_right_trans.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/beegee-tokyo/CCTVview/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_arrow_right_trans.png
--------------------------------------------------------------------------------
/.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/encodings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/lint.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/java/tk/giesecke/cctvview/Rtsp/Audio/ACCStream.java:
--------------------------------------------------------------------------------
1 | package tk.giesecke.cctvview.Rtsp.Audio;
2 |
3 | /**
4 | * Not implemented yet
5 | */
6 | @SuppressWarnings("unused")
7 | public class ACCStream extends AudioStream {
8 | }
9 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Wed Jan 24 09:45:35 SGT 2018
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip
7 |
--------------------------------------------------------------------------------
/.idea/inspectionProfiles/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #A9A9A9
4 | #696969
5 | #000000
6 | #00000000
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/java/tk/giesecke/cctvview/Onvif/OnvifDeviceDNS.java:
--------------------------------------------------------------------------------
1 | package tk.giesecke.cctvview.Onvif;
2 |
3 | public class OnvifDeviceDNS {
4 |
5 | @SuppressWarnings("SameReturnValue")
6 | public static String getDNSCommand() {
7 | return "";
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/app/src/main/java/tk/giesecke/cctvview/Onvif/OnvifPtzNodes.java:
--------------------------------------------------------------------------------
1 | package tk.giesecke.cctvview.Onvif;
2 |
3 | public class OnvifPtzNodes {
4 |
5 | @SuppressWarnings("SameReturnValue")
6 | public static String getNodesCommand() {
7 | return "";
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/app/src/main/java/tk/giesecke/cctvview/Onvif/OnvifPtzNode.java:
--------------------------------------------------------------------------------
1 | package tk.giesecke.cctvview.Onvif;
2 |
3 | public class OnvifPtzNode {
4 |
5 | public static String getNodeCommand(String profileToken) {
6 | return "" + profileToken + "";
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/app/src/main/java/tk/giesecke/cctvview/Onvif/OnvifPtzConfigurations.java:
--------------------------------------------------------------------------------
1 | package tk.giesecke.cctvview.Onvif;
2 |
3 | public class OnvifPtzConfigurations {
4 |
5 | @SuppressWarnings("SameReturnValue")
6 | public static String getConfigsCommand() {
7 | return "";
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/app/src/main/java/tk/giesecke/cctvview/Onvif/OnvifDeviceNetworkProtocols.java:
--------------------------------------------------------------------------------
1 | package tk.giesecke.cctvview.Onvif;
2 |
3 | public class OnvifDeviceNetworkProtocols {
4 |
5 | @SuppressWarnings("SameReturnValue")
6 | public static String getNetProtocolsCommand() {
7 | return "";
8 | }
9 |
10 | }
11 |
--------------------------------------------------------------------------------
/app/src/main/java/tk/giesecke/cctvview/Onvif/OnvifDeviceNetworkDefaultGateway.java:
--------------------------------------------------------------------------------
1 | package tk.giesecke.cctvview.Onvif;
2 |
3 | public class OnvifDeviceNetworkDefaultGateway {
4 |
5 | @SuppressWarnings("SameReturnValue")
6 | public static String getNetGatewayCommand() {
7 | return "";
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/app/src/main/java/tk/giesecke/cctvview/Onvif/OnvifMediaOSDs.java:
--------------------------------------------------------------------------------
1 | package tk.giesecke.cctvview.Onvif;
2 |
3 | public class OnvifMediaOSDs {
4 |
5 | public static String getOSDsCommand(String profileToken) {
6 | String getOsdsCmd = "";
7 | getOsdsCmd += "" + profileToken + "";
8 | return getOsdsCmd;
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/app/src/main/java/tk/giesecke/cctvview/Onvif/OnvifPtzConfiguration.java:
--------------------------------------------------------------------------------
1 | package tk.giesecke.cctvview.Onvif;
2 |
3 | public class OnvifPtzConfiguration {
4 |
5 | public static String getConfigCommand(String profileToken) {
6 | String contStopCmd = "";
7 | contStopCmd += "" + profileToken + "";
8 | return contStopCmd;
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/app/src/main/java/tk/giesecke/cctvview/Onvif/OnvifPtzStop.java:
--------------------------------------------------------------------------------
1 | package tk.giesecke.cctvview.Onvif;
2 |
3 | public class OnvifPtzStop {
4 |
5 | public static String getStopCommand(String profileToken) {
6 | return "" +
7 | "" + profileToken + "" +
8 | "" +
9 | "true" +
10 | "" +
11 | "" +
12 | "true" +
13 | "" +
14 | "";
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/app/src/main/java/tk/giesecke/cctvview/Rtsp/Audio/AudioStream.java:
--------------------------------------------------------------------------------
1 | package tk.giesecke.cctvview.Rtsp.Audio;
2 |
3 | import android.util.Log;
4 |
5 | import tk.giesecke.cctvview.BuildConfig;
6 | import tk.giesecke.cctvview.CCTVview;
7 | import tk.giesecke.cctvview.Rtsp.Stream.RtpStream;
8 |
9 | /**
10 | *
11 | */
12 | public abstract class AudioStream extends RtpStream {
13 |
14 | protected void recombinePacket(StreamPacks streamPacks) {
15 | if (BuildConfig.DEBUG) Log.d(CCTVview.DEBUG_LOG_TAG, "AudioStream - recombinePacket");
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/.idea/runConfigurations.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
11 |
12 |
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/app/src/main/java/tk/giesecke/cctvview/BootReceiver.java:
--------------------------------------------------------------------------------
1 | package tk.giesecke.cctvview;
2 |
3 | import android.content.BroadcastReceiver;
4 | import android.content.Context;
5 | import android.content.Intent;
6 | import android.os.Handler;
7 |
8 | public class BootReceiver extends BroadcastReceiver {
9 |
10 | Context bootContext;
11 |
12 | @Override
13 | public void onReceive(Context context, Intent intent) {
14 | bootContext = context;
15 | Handler mHandler = new Handler();
16 | mHandler.postDelayed(new Runnable() {
17 | @Override
18 | public void run() {
19 | Intent myIntent = new Intent(bootContext, CCTVview.class);
20 | myIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
21 | bootContext.startActivity(myIntent);
22 | }
23 | },10000);
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | org.gradle.jvmargs=-Xmx1536m
13 |
14 | # When configured, Gradle will run in incubating parallel mode.
15 | # This option should only be used with decoupled projects. More details, visit
16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
17 | # org.gradle.parallel=true
18 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/my_menu_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/app/src/main/java/tk/giesecke/cctvview/Onvif/OnvifPtzAbsoluteMove.java:
--------------------------------------------------------------------------------
1 | package tk.giesecke.cctvview.Onvif;
2 |
3 | public class OnvifPtzAbsoluteMove {
4 |
5 | @SuppressWarnings("SameParameterValue")
6 | public static String getAbsoluteMoveCommand(int xMove, int yMove, int zoomVal, String profileToken) {
7 | String contMoveCmd = "";
8 | contMoveCmd += "" + profileToken + "";
9 | contMoveCmd += "";
10 | contMoveCmd += "";
15 | contMoveCmd += "";
18 | contMoveCmd += "";
19 | contMoveCmd += "";
20 | return contMoveCmd;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/app/src/main/java/tk/giesecke/cctvview/Onvif/OnvifPtzContinuousMove.java:
--------------------------------------------------------------------------------
1 | package tk.giesecke.cctvview.Onvif;
2 |
3 | class OnvifPtzContinuousMove {
4 |
5 | @SuppressWarnings({"SameParameterValue", "unused"})
6 | public static String getContinuousMoveCommand(int xMove, int yMove, int zoomVal, String profileToken) {
7 | String contMoveCmd = "";
8 | contMoveCmd += "" + profileToken + "";
9 | contMoveCmd += "";
10 | contMoveCmd += "";
15 | contMoveCmd += "";
18 | contMoveCmd += "";
19 | contMoveCmd += "";
20 | return contMoveCmd;
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # CCTVview
2 | Simple ONVIF RTSP camera viewer for Android
3 |
4 | # IMPORTANT
5 | This started as a project to search and access ONVIF compatible cameras.
6 | Unfortunately the manufacturer GCWells crippled down the ONVIF compatibility, so this code is no longer a good example for detecting/accessing an ONVIF camera, but it works with my specific camera.
7 | In addition I added a feature to show data from my local solar panel monitor, which will be useless for others.
8 |
9 | Features:
10 | - Searches for ONVIF compatible cameras on the LAN.
11 | - Request and parse ONVIF Scopes, Device Information, Device Capabilities, ... from the camera
12 | - View RTSP stream from ONVIF camera
13 |
14 | Missing:
15 | - Not all ONVIF commands/requests are supported
16 | - Only video stream is available, audio stream is not implemented
17 |
18 | # External sources
19 | htwahzs/Rtsp-Android-Client ==> https://github.com/htwahzs/Rtsp-Android-Client
20 | ilovecmu/android-rtsp-client-hw-decoder-demo ==> https://github.com/ilovecmu/android-rtsp-client-hw-decoder-demo
21 |
--------------------------------------------------------------------------------
/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # By default, the flags in this file are appended to flags specified
3 | # in D:\Android-Dev\android-studio-others\sdk/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
6 | #
7 | # For more details, see
8 | # http://developer.android.com/guide/developing/tools/proguard.html
9 |
10 | # Add any project specific keep options here:
11 |
12 | # If your project uses WebView with JS, uncomment the following
13 | # and specify the fully qualified class name to the JavaScript interface
14 | # class:
15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
16 | # public *;
17 | #}
18 |
19 | # Uncomment this to preserve the line number information for
20 | # debugging stack traces.
21 | #-keepattributes SourceFile,LineNumberTable
22 |
23 | # If you keep the line number information, uncomment this to
24 | # hide the original source file name.
25 | #-renamesourcefileattribute SourceFile
26 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/onvif_discovery.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
15 |
16 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/app/src/main/java/tk/giesecke/cctvview/Onvif/OnvifDevice.java:
--------------------------------------------------------------------------------
1 | package tk.giesecke.cctvview.Onvif;
2 |
3 | import java.net.URL;
4 |
5 | @SuppressWarnings("unused")
6 | public class OnvifDevice {
7 | public URL discoveredURL = null;
8 | public String baseUrl = "";
9 | public String webURL = "";
10 | public String webPath = "";
11 | public int webPort = -1;
12 | public String rtspURL = "";
13 | public String rtspPath = "";
14 | public int rtspPort = -1;
15 | public String userName = "";
16 | public String passWord = "";
17 | public OnvifDeviceScopes scopes = null;
18 | public OnvifDeviceInformation devInfo = null;
19 | public OnvifDeviceCapabilities devCapabilities = null;
20 | public OnvifDeviceDNS devDNS = null;
21 | public OnvifDeviceNetworkDefaultGateway devDefaultGateway = null;
22 | public OnvifDeviceNetworkInterfaces devNetInterface = null;
23 | public OnvifDeviceNetworkProtocols devNetProtocols = null;
24 | public final OnvifMediaProfiles[] mediaProfiles = {null, null};
25 | public OnvifMediaStreamUri mediaStreamUri = null;
26 | public OnvifMediaOSDs mediaOSDs = null;
27 | public OnvifPtzNodes ptzNodes = null;
28 | public OnvifPtzNode ptzNode = null;
29 | public OnvifPtzConfigurations ptzConfigs = null;
30 | public OnvifPtzConfiguration ptzConfig = null;
31 | }
32 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/onvif_settings_menu.xml:
--------------------------------------------------------------------------------
1 |
2 |
40 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 25
5 | buildToolsVersion "25.0.2"
6 | defaultConfig {
7 | applicationId "tk.giesecke.cctvview"
8 | minSdkVersion 16
9 | //noinspection OldTargetApi
10 | targetSdkVersion 25
11 | versionCode 1
12 | versionName "1.0"
13 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
14 | }
15 | buildTypes {
16 | release {
17 | minifyEnabled false
18 | // proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
19 | }
20 | }
21 | sourceSets {
22 | main {
23 | java.srcDirs = ['src/main/java', 'src/main/java/OnvifResponses', 'src/main/java/tk/giesecke/RtspClient']
24 | }
25 | }
26 | productFlavors {
27 | }
28 | }
29 |
30 | repositories {
31 | flatDir {
32 | dirs 'libs'
33 | }
34 | }
35 |
36 | dependencies {
37 | compile fileTree(include: ['.jar', '.so'], dir: 'libs')
38 | compile 'com.android.support:appcompat-v7:25.3.1'
39 | compile 'com.android.support:design:25.3.1'
40 | compile 'com.android.support.constraint:constraint-layout:1.0.2'
41 | compile 'com.squareup.okio:okio:1.12.0'
42 | compile 'com.squareup.okhttp3:okhttp:3.7.0'
43 | compile 'com.android.support:support-v4:25.3.1'
44 | compile 'org.eclipse.ecf:org.apache.commons.codec:1.9.0.v20170208-1614'
45 | compile 'com.github.andriydruk:dnssd:0.9.1'
46 | compile 'com.github.andriydruk:rxdnssd:0.9.1'
47 | compile(name: 'mina-core-2.0.7', ext: 'jar')
48 | }
49 |
--------------------------------------------------------------------------------
/app/src/main/java/tk/giesecke/cctvview/Onvif/OnvifMediaStreamUri.java:
--------------------------------------------------------------------------------
1 | package tk.giesecke.cctvview.Onvif;
2 |
3 | import java.net.URI;
4 |
5 | import static tk.giesecke.cctvview.Onvif.OnvifResponseParser.timeOutToMS;
6 |
7 | public class OnvifMediaStreamUri {
8 |
9 | public String mediaUri = "unknown";
10 | private String streamTimeout = "unknown";
11 | private int streamTimeoutMS = 0;
12 | public int mediaRtspPort = 0;
13 |
14 | @SuppressWarnings("SameReturnValue")
15 | public static String getStreamUriCommand() {
16 | return "";
17 | }
18 |
19 | public static boolean parseStreamUriResponse(String response, OnvifMediaStreamUri parsed) {
20 | try {
21 | OnvifResponseParser.lastIndex = 0; // Start from beginning of response
22 | URI foundURI = URI.create(OnvifResponseParser.parseOnvifString(":Uri>", "", "
2 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
35 |
38 |
39 |
43 |
44 |
45 |
49 |
50 |
51 |
55 |
56 |
57 |
58 |
59 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/onvif_menu.xml:
--------------------------------------------------------------------------------
1 |
2 |
72 |
--------------------------------------------------------------------------------
/app/src/main/java/tk/giesecke/cctvview/Onvif/OnvifDeviceInformation.java:
--------------------------------------------------------------------------------
1 | package tk.giesecke.cctvview.Onvif;
2 |
3 | /**
4 | * Created from https://www.onvif.org/ver10/device/wsdl/devicemgmt.wsdl
5 | *
6 | * GetDeviceInformation
7 | * Description:
8 | * This operation gets basic device information from the device.
9 | *
10 | * Input:
11 | * [GetDeviceInformation]
12 | *
13 | * Output:
14 | * [GetDeviceInformationResponse]
15 | * Manufacturer [string] - The manufactor of the device.
16 | * Model [string] - The device model.
17 | * FirmwareVersion [string] - The firmware version in the device.
18 | * SerialNumber [string] - The serial number of the device.
19 | * HardwareId [string] - The hardware ID of the device.
20 | */
21 |
22 | public class OnvifDeviceInformation {
23 | private String manufacturerName = "unknown";
24 | private String modelName = "unknown";
25 | private String fwVersion = "unknown";
26 | private String serialNumber = "unknown";
27 | private String hwID = "unknown";
28 |
29 | @SuppressWarnings("SameReturnValue")
30 | public static String getDeviceInformationCommand() {
31 | return "";
32 | }
33 |
34 | public static boolean parseDeviceInformationResponse(String response, OnvifDeviceInformation parsed) {
35 | try {
36 | OnvifResponseParser.lastIndex = 0; // Start from beginning of response
37 | parsed.manufacturerName = OnvifResponseParser.parseOnvifString("facturer>", "", "", "", "", "";
17 | }
18 |
19 | public static boolean parseNetworkInterfacesResponse(String response, OnvifDeviceNetworkInterfaces parsed) {
20 | try {
21 | OnvifResponseParser.lastIndex = 0; // Start from beginning of response
22 | parsed.netInterfaceToken = OnvifResponseParser.parseOnvifString("token=\"", "\">", response);
23 | parsed.netInterfaceEnabled = OnvifResponseParser.parseOnvifBoolean("Enabled>", "", "", "", "", "", "", "";
38 | }
39 |
40 | public static boolean parseScopesResponse(String response, OnvifDeviceScopes parsed) {
41 |
42 | try {
43 | OnvifResponseParser.lastIndex = 0; // Start from beginning of response
44 | parsed.scopeType = OnvifResponseParser.parseOnvifString("/type/", "
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/app/src/main/java/tk/giesecke/cctvview/Rtsp/Socket/RtcpSocket.java:
--------------------------------------------------------------------------------
1 | package tk.giesecke.cctvview.Rtsp.Socket;
2 |
3 | import android.os.Handler;
4 | import android.os.HandlerThread;
5 | import android.util.Log;
6 |
7 | import java.io.IOException;
8 | import java.net.DatagramPacket;
9 | import java.net.DatagramSocket;
10 | import java.net.InetAddress;
11 | import java.net.SocketException;
12 | import java.net.UnknownHostException;
13 |
14 | import tk.giesecke.cctvview.BuildConfig;
15 | import tk.giesecke.cctvview.CCTVview;
16 |
17 | /**
18 | *This class is used to send the rtcp packet to the server
19 | */
20 | class RtcpSocket {
21 |
22 | private DatagramSocket mSocket;
23 | private DatagramPacket mPacket;
24 | private Handler mHandler;
25 | private String serverIp;
26 | private int serverPort;
27 | private boolean isStoped;
28 | private HandlerThread thread;
29 |
30 | RtcpSocket(int port, String serverIp, int serverPort) {
31 | if (BuildConfig.DEBUG) Log.d(CCTVview.DEBUG_LOG_TAG, "RtcpSocket");
32 | try {
33 | this.serverIp = serverIp;
34 | this.serverPort = serverPort;
35 | mSocket = new DatagramSocket(port);
36 | byte[] message = new byte[2048];
37 | mPacket = new DatagramPacket(message, message.length);
38 | thread = new HandlerThread("RTCPSocketThread");
39 | thread.start();
40 | isStoped = false;
41 | mHandler = new Handler(thread.getLooper());
42 | } catch ( SocketException e ) {
43 | if (BuildConfig.DEBUG) Log.d(CCTVview.DEBUG_LOG_TAG, "RtcpSocket Exception " + e.getMessage());
44 | }
45 | }
46 |
47 | public void start() {
48 | mHandler.post(new Runnable() {
49 | @Override
50 | public void run() {
51 | while ( !isStoped ) {
52 | try {
53 | if (mSocket != null) {
54 | mSocket.receive(mPacket);
55 | } else {
56 | if (BuildConfig.DEBUG) Log.d(CCTVview.DEBUG_LOG_TAG, "RtcpSocket start mSocket was NULL");
57 | }
58 | } catch (IOException e) {
59 | if (BuildConfig.DEBUG) Log.d(CCTVview.DEBUG_LOG_TAG, "RtcpSocket start receive Exception " + e.getMessage());
60 | }
61 | // if (BuildConfig.DEBUG) Log.d(CCTVview.DEBUG_LOG_TAG, "Rtcp rev package " + mPacket.toString());
62 | }
63 | }
64 | });
65 | }
66 |
67 | void stop(){
68 | if (BuildConfig.DEBUG) Log.d(CCTVview.DEBUG_LOG_TAG, "RtcpSocket stop");
69 | mSocket.close();
70 | mSocket = null;
71 | mPacket = null;
72 | isStoped = true;
73 | thread.quit();
74 | }
75 |
76 | void sendReceiverReport() {
77 | new Thread(new Runnable() {
78 | @Override
79 | public void run() {
80 | startSendReport();
81 | }
82 | }).start();
83 | }
84 |
85 | private void startSendReport() {
86 | byte[] sendData = new byte[2];
87 | sendData[0] = (byte) Integer.parseInt("10000000",2);
88 | sendData[1] = (byte) 201;
89 | try {
90 | mPacket = new DatagramPacket(sendData,sendData.length, InetAddress.getByName(serverIp),serverPort);
91 | } catch (UnknownHostException e) {
92 | if (BuildConfig.DEBUG) Log.d(CCTVview.DEBUG_LOG_TAG, "startSendReport Exception " + e.getMessage());
93 | }
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/app/src/main/res/layout-land/cctv_view.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
37 |
38 |
48 |
49 |
58 |
59 |
70 |
71 |
81 |
82 |
83 |
84 |
85 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/cctv_view.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
37 |
38 |
47 |
48 |
59 |
60 |
71 |
72 |
82 |
83 |
84 |
85 |
86 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | CCTVview
3 | ONVIF commands
4 | Select player
5 | DevInfo
6 | Scopes
7 | Capabilities
8 | Profiles
9 | Default Gateway
10 | DNS Information
11 | Network Interfaces
12 | Network Protocols
13 | Video stream URL
14 | OSD info
15 | PTZ Get all Nodes
16 | PTZ Get Node
17 | PTZ Get all Config
18 | PTZ Get Config
19 | PTZ stop
20 | PTZ absolute 0,0
21 |
22 | Settings
23 |
24 |
25 | Camera settings
26 |
27 | Camera IP: Hostname or IP address of the camera.\n
28 | Username: Username for camera access.\n
29 | Password: Password for camera access.\n
30 | RTSP path: Path to the RTSP stream.\n
31 | RTSP port number: Port for RTSP streaming.\n
32 | ONVIF path: Path to the camera web access.\n
33 | ONVIF port number: Port for Web/PTZ control.
34 |
35 |
36 | Camera IP
37 | 192.168.0.55
38 |
39 | Username
40 | admin
41 |
42 | Password
43 | 123
44 |
45 | RTSP path
46 | /onvif1
47 |
48 | RTSP port
49 | 554
50 |
51 | Web/Onvif path
52 | /onvif/device_service
53 |
54 | Web/Onvif port
55 | 5000
56 |
57 | Searching for cameras, please wait…
58 | No cameras\'s found, but you can still try to add a camera manually!
59 |
60 | MHC
61 | MHC2
62 | Not on home WiFi, no viewing possible
63 | Could not connect to camera
64 | OnvifDiscovery
65 | OnvifSettings
66 |
67 | 192.168.0.59
68 | 192.168.0.52
69 | 192.168.0.53
70 |
71 | \?W
72 |
73 |
74 |
--------------------------------------------------------------------------------
/app/src/main/java/tk/giesecke/cctvview/Onvif/OnvifResponseParser.java:
--------------------------------------------------------------------------------
1 | package tk.giesecke.cctvview.Onvif;
2 |
3 | class OnvifResponseParser {
4 |
5 | static int lastIndex;
6 | private static String testValue;
7 | private static String partParsed;
8 |
9 | /**
10 | * static int parseOnvifInt
11 | * Return Integer from ONVIF element
12 | * @param start
13 | * Start parsing from start
14 | * @param end
15 | * End parsing from end
16 | * @param search
17 | * String to search
18 | * @return
19 | * Found value as string
20 | */
21 | static int parseOnvifInt(String start, String end, String search) {
22 | // search for 'start'
23 | // until 'end'
24 | // in 'search'
25 | if (search.contains(start)) {
26 | int startIndex = search.indexOf(start, lastIndex)+ start.length();
27 | partParsed = search.substring(startIndex);
28 | int endIndex = partParsed.indexOf(end);
29 | lastIndex = startIndex + endIndex;
30 | testValue = partParsed.substring(0,endIndex);
31 | try {
32 | return Integer.valueOf(testValue);
33 | } catch (NumberFormatException ignore) {}
34 | }
35 | return -1;
36 | }
37 |
38 | static String parseOnvifString(String start, String end, String search) {
39 | // search for 'start'
40 | // until 'end'
41 | // in 'search'
42 | if (search.contains(start)) {
43 | int startIndex = search.indexOf(start, lastIndex)+ start.length();
44 | partParsed = search.substring(startIndex);
45 | int endIndex = partParsed.indexOf(end);
46 | lastIndex = startIndex + endIndex;
47 | testValue = partParsed.substring(0,endIndex);
48 | return testValue;
49 | } else {
50 | return "n/a";
51 | }
52 | }
53 |
54 | @SuppressWarnings("SameParameterValue")
55 | static boolean parseOnvifBoolean(String start, String end, String search) {
56 | // search for 'start'
57 | // until 'end'
58 | // in 'search'
59 | if (search.contains(start)) {
60 | int startIndex = search.indexOf(start, lastIndex)+ start.length();
61 | partParsed = search.substring(startIndex);
62 | int endIndex = partParsed.indexOf(end);
63 | lastIndex = startIndex + endIndex;
64 | testValue = partParsed.substring(0,endIndex);
65 | if (testValue.equalsIgnoreCase("true")) {
66 | return true;
67 | }
68 | }
69 | return false;
70 | }
71 |
72 | @SuppressWarnings("SameParameterValue")
73 | static boolean parseOnvifHasValue(String value, String search) {
74 | return search.contains(value);
75 | }
76 |
77 | static int timeOutToMS(String timeoutString) {
78 | String testValue;
79 | int valStart;
80 | int valEnd;
81 | int timeoutHours = 0;
82 | int timeoutMinutes = 0;
83 | float timeoutSeconds = 0;
84 |
85 | if (!timeoutString.contains("PT")) { // No valid timeout value found
86 | return -1;
87 | }
88 | valStart = timeoutString.indexOf("PT") + 2;
89 | if (timeoutString.contains("H")) {
90 | valEnd = timeoutString.indexOf("H");
91 | testValue = timeoutString.substring(valStart, valEnd);
92 | try {
93 | timeoutHours = Integer.valueOf(testValue);
94 | } catch (NumberFormatException ignore) {}
95 | valStart = valEnd + 1;
96 | }
97 |
98 | if (timeoutString.contains("M")) {
99 | valEnd = timeoutString.indexOf("M");
100 | testValue = timeoutString.substring(valStart, valEnd);
101 | try {
102 | timeoutMinutes = Integer.valueOf(testValue);
103 | } catch (NumberFormatException ignore) {}
104 | valStart = valEnd + 1;
105 | }
106 |
107 | if (timeoutString.contains("S")) {
108 | valEnd = timeoutString.indexOf("S");
109 | testValue = timeoutString.substring(valStart, valEnd);
110 | try {
111 | timeoutSeconds = Float.valueOf(testValue);
112 | } catch (NumberFormatException ignore) {}
113 | }
114 |
115 | float timeoutMS = ((timeoutHours * 3600) + (timeoutMinutes * 60) + timeoutSeconds) * 1000;
116 | return (int) timeoutMS;
117 | }
118 | }
119 |
--------------------------------------------------------------------------------
/app/src/main/java/tk/giesecke/cctvview/Rtsp/Stream/RtpStream.java:
--------------------------------------------------------------------------------
1 | package tk.giesecke.cctvview.Rtsp.Stream;
2 |
3 | import android.os.HandlerThread;
4 | import android.os.Handler;
5 | import android.util.Log;
6 |
7 | import java.util.concurrent.LinkedBlockingDeque;
8 |
9 | import tk.giesecke.cctvview.BuildConfig;
10 | import tk.giesecke.cctvview.CCTVview;
11 |
12 | /**
13 | *This class is used to analysis the data from rtp socket , recombine it to video or audio stream
14 | * 1. get the data from rtp socket
15 | * 2. put the data into buffer
16 | * 3. use the thread to get the data from buffer, and unpack it
17 | */
18 | public abstract class RtpStream {
19 |
20 | // Unused as audio streams are not implemented!!!!
21 | // protected final static int TRACK_VIDEO = 0x01;
22 | // protected final static int TRACK_AUDIO = 0x02;
23 |
24 | private final Handler mHandler;
25 | private byte[] buffer;
26 | private final HandlerThread thread;
27 | private boolean isStoped;
28 |
29 | protected class StreamPacks {
30 | // boolean mark;
31 | public int pt;
32 | // long timestamp;
33 | // int sequenceNumber;
34 | // long Ssrc;
35 | public byte[] data;
36 | }
37 |
38 | private static class bufferUnit {
39 | public byte[] data;
40 | int len;
41 | }
42 |
43 | private static final LinkedBlockingDeque bufferQueue = new LinkedBlockingDeque<>();
44 |
45 | protected RtpStream() {
46 | thread = new HandlerThread("RTPStreamThread");
47 | thread.start();
48 | mHandler = new Handler(thread.getLooper());
49 | unpackThread();
50 | isStoped = false;
51 | }
52 |
53 | public static void receiveData(byte[] data, int len) {
54 | bufferUnit tmpBuffer = new bufferUnit();
55 | tmpBuffer.data = new byte[len];
56 | System.arraycopy(data,0,tmpBuffer.data,0,len);
57 | tmpBuffer.len = len;
58 | try {
59 | bufferQueue.put(tmpBuffer);
60 | } catch (InterruptedException e) {
61 | if (BuildConfig.DEBUG) Log.d(CCTVview.DEBUG_LOG_TAG, "RtpStream - receiveData exception: " + e.getMessage());
62 | }
63 | }
64 |
65 | private void unpackThread() {
66 | mHandler.post(new Runnable() {
67 | @Override
68 | public void run() {
69 | bufferUnit tmpBuffer;
70 | while (!isStoped) {
71 | try {
72 | tmpBuffer = bufferQueue.take();
73 | buffer = new byte[tmpBuffer.len];
74 | System.arraycopy(tmpBuffer.data,0,buffer,0,tmpBuffer.len);
75 | unpackData();
76 | } catch (InterruptedException e) {
77 | if (BuildConfig.DEBUG) Log.d(CCTVview.DEBUG_LOG_TAG, "RtpStream - unpackThread exception: " + e.getMessage());
78 | }
79 | }
80 | buffer = null;
81 | bufferQueue.clear();
82 | }
83 | });
84 | }
85 |
86 | protected void stop(){
87 | isStoped = true;
88 | try {
89 | Thread.sleep(100);
90 | } catch (InterruptedException e) {
91 | if (BuildConfig.DEBUG) Log.d(CCTVview.DEBUG_LOG_TAG, "RtpStream - stop exception: " + e.getMessage());
92 | }
93 | bufferQueue.clear();
94 | buffer = null;
95 | thread.quit();
96 | }
97 |
98 |
99 | protected abstract void recombinePacket(StreamPacks sp);
100 |
101 | private void unpackData() {
102 | StreamPacks tmpStreampack = new StreamPacks();
103 | if(buffer.length == 0) return;
104 | int rtpVersion = (buffer[0]&0xFF)>>6;
105 | if(rtpVersion != 2) {
106 | if (BuildConfig.DEBUG) Log.d(CCTVview.DEBUG_LOG_TAG, "RtpStream This is not a rtp packet.");
107 | return;
108 | }
109 | // tmpStreampack.mark = (buffer[1] & 0xFF & 0x80) >> 7 == 1;
110 | tmpStreampack.pt = buffer[1] & 0x7F;
111 | // tmpStreampack.sequenceNumber = ((buffer[2] & 0xFF) << 8) | (buffer[3] & 0xFF);
112 | // tmpStreampack.timestamp = Long.parseLong(Integer.toHexString(((buffer[4] & 0xFF) << 24) | ((buffer[5]&0xFF) << 16) | ((buffer[6]&0xFF) << 8) | (buffer[7]&0xFF)) , 16);
113 | // tmpStreampack.Ssrc = ((buffer[8]&0xFF) << 24) | ((buffer[9]&0xFF) << 16) | ((buffer[10]&0xFF) << 8) | (buffer[11]&0xFF);
114 | tmpStreampack.data = new byte[buffer.length-12];
115 |
116 | System.arraycopy(buffer, 12, tmpStreampack.data, 0, buffer.length - 12);
117 |
118 | recombinePacket(tmpStreampack);
119 | }
120 | }
121 |
--------------------------------------------------------------------------------
/app/src/main/java/tk/giesecke/cctvview/Rtsp/Video/VideoStream.java:
--------------------------------------------------------------------------------
1 | package tk.giesecke.cctvview.Rtsp.Video;
2 |
3 | import tk.giesecke.cctvview.Rtsp.Stream.RtpStream;
4 | import tk.giesecke.cctvview.Rtsp.RtspClient;
5 |
6 | /**
7 | *
8 | */
9 | public abstract class VideoStream extends RtpStream {
10 | private final static int NAL_UNIT_TYPE_STAP_A = 24;
11 | private final static int NAL_UNIT_TYPE_STAP_B = 25;
12 | private final static int NAL_UNIT_TYPE_MTAP16 = 26;
13 | private final static int NAL_UNIT_TYPE_MTAP24 = 27;
14 | private final static int NAL_UNIT_TYPE_FU_A = 28;
15 | private final static int NAL_UNIT_TYPE_FU_B = 29;
16 |
17 | byte[] NALUnit;
18 | private boolean NALEndFlag;
19 | private final byte[][] buffer = new byte[1024][];
20 | private int bufferLength;
21 | RtspClient.SDPInfo mSDPinfo;
22 | private int packetNum = 0;
23 |
24 | VideoStream() {
25 | NALEndFlag = false;
26 | buffer[0] = new byte[1];
27 | }
28 |
29 | protected abstract void decodeH264Stream();
30 |
31 | @Override
32 | protected void recombinePacket(StreamPacks streamPacks) {
33 | if(streamPacks.pt == 96) H264PacketRecombine(streamPacks);
34 | }
35 |
36 | //This method is used to get the NAL unit from the RTP packet
37 | //Then translate the NAL unit to the decoder
38 | private void H264PacketRecombine(StreamPacks streamPacks) {
39 | int tmpLen;
40 | int nalType = streamPacks.data[0] & 0x1F;
41 | int packFlag = streamPacks.data[1] & 0xC0;
42 |
43 | switch (nalType) {
44 |
45 | //Single-timeaggregation packet
46 | case NAL_UNIT_TYPE_STAP_A:
47 | break;
48 |
49 | //Single-timeaggregation packet
50 | case NAL_UNIT_TYPE_STAP_B:
51 | break;
52 |
53 | //Multi-time aggregationpacket
54 | case NAL_UNIT_TYPE_MTAP16:
55 | break;
56 |
57 | //Multi-time aggregationpacket
58 | case NAL_UNIT_TYPE_MTAP24:
59 | break;
60 |
61 | //Fragmentationunit
62 | case NAL_UNIT_TYPE_FU_A:
63 | switch (packFlag) {
64 | //NAL Unit start packet
65 | case 0x80:
66 | NALEndFlag = false;
67 | packetNum = 1;
68 | bufferLength = streamPacks.data.length-1 ;
69 | buffer[1] = new byte[streamPacks.data.length-1];
70 | buffer[1][0] = (byte)((streamPacks.data[0] & 0xE0)|(streamPacks.data[1]&0x1F));
71 | System.arraycopy(streamPacks.data,2,buffer[1],1,streamPacks.data.length-2);
72 | break;
73 | //NAL Unit middle packet
74 | case 0x00:
75 | NALEndFlag = false;
76 | packetNum++;
77 | bufferLength += streamPacks.data.length-2;
78 | buffer[packetNum] = new byte[streamPacks.data.length-2];
79 | System.arraycopy(streamPacks.data,2,buffer[packetNum],0,streamPacks.data.length-2);
80 | break;
81 | //NAL Unit end packet
82 | case 0x40:
83 | NALEndFlag = true;
84 | NALUnit = new byte[bufferLength + streamPacks.data.length + 2];
85 | NALUnit[0] = 0x00;
86 | NALUnit[1] = 0x00;
87 | NALUnit[2] = 0x00;
88 | NALUnit[3] = 0x01;
89 | tmpLen = 4;
90 | try {
91 | System.arraycopy(buffer[1], 0, NALUnit, tmpLen, buffer[1].length);
92 | tmpLen += buffer[1].length;
93 | for(int i = 2; i < packetNum+1; ++i) {
94 | System.arraycopy(buffer[i],0,NALUnit,tmpLen,buffer[i].length);
95 | tmpLen += buffer[i].length;
96 | }
97 | System.arraycopy(streamPacks.data,2,NALUnit,tmpLen,streamPacks.data.length-2);
98 | } catch (NullPointerException ignore) {} // Maybe we are stopping
99 | break;
100 | }
101 | break;
102 |
103 | //Fragmentationunit
104 | case NAL_UNIT_TYPE_FU_B:
105 | break;
106 |
107 | //Single NAL unit per packet
108 | default:
109 | NALUnit = new byte[4+streamPacks.data.length];
110 | NALUnit[0] = 0x00;
111 | NALUnit[1] = 0x00;
112 | NALUnit[2] = 0x00;
113 | NALUnit[3] = 0x01;
114 | System.arraycopy(streamPacks.data,0,NALUnit,4,streamPacks.data.length);
115 | NALEndFlag = true;
116 | break;
117 | }
118 | if(NALEndFlag){
119 | decodeH264Stream();
120 | }
121 | }
122 |
123 | protected void stop() {
124 | NALUnit = null;
125 | super.stop();
126 | }
127 | }
128 |
--------------------------------------------------------------------------------
/app/src/main/java/tk/giesecke/cctvview/Onvif/OnvifHeaderBody.java:
--------------------------------------------------------------------------------
1 | package tk.giesecke.cctvview.Onvif;
2 |
3 | import android.annotation.SuppressLint;
4 |
5 | import org.apache.commons.codec.digest.MessageDigestAlgorithms;
6 | import org.apache.mina.util.Base64;
7 |
8 | import java.security.MessageDigest;
9 | import java.security.NoSuchAlgorithmException;
10 | import java.text.SimpleDateFormat;
11 | import java.util.Calendar;
12 | import java.util.GregorianCalendar;
13 | import java.util.Random;
14 | import java.util.SimpleTimeZone;
15 | import java.util.TimeZone;
16 |
17 | import static tk.giesecke.cctvview.CCTVview.selectedDevice;
18 |
19 | public class OnvifHeaderBody {
20 |
21 | private static String utcTime;
22 | private static String nonce;
23 |
24 | public static String getAuthorizationHeader() {
25 | nonce = "" + new Random().nextInt();
26 | utcTime = getUTCTime();
27 | String envelopePart;
28 | String authorizationPart = "";
29 | envelopePart = "";
50 | if (!selectedDevice.userName.equalsIgnoreCase("")) {
51 | authorizationPart = "" +
52 | "" +
53 | "" +
54 | "" +
55 | utcTime +
56 | "" +
57 | "" +
58 | "" +
59 | "" +
60 | selectedDevice.userName +
61 | "" +
62 | "" +
63 | encryptPassword(selectedDevice.passWord) +
64 | "" +
65 | "" +
66 | nonce +
67 | "" +
68 | "" +
69 | utcTime +
70 | "" +
71 | "" +
72 | "" +
73 | "";
74 | }
75 | return envelopePart + authorizationPart + "";
76 | }
77 |
78 | @SuppressWarnings("SameReturnValue")
79 | public static String getEnvelopeEnd() {
80 | return "";
81 | }
82 |
83 | private static String encryptPassword(String password) {
84 | String beforeEncryption = nonce + utcTime + password;
85 | byte[] encryptedRaw;
86 | try {
87 | MessageDigest SHA1 = MessageDigest.getInstance(MessageDigestAlgorithms.SHA_1);
88 | SHA1.reset();
89 | SHA1.update(beforeEncryption.getBytes());
90 | encryptedRaw = SHA1.digest();
91 | }
92 | catch (NoSuchAlgorithmException e) {
93 | e.printStackTrace();
94 | return null;
95 | }
96 | String encodedString = new String(Base64.encodeBase64(encryptedRaw));
97 | encodedString = encodedString.replace('+','-').replace('/','_');
98 | return encodedString;
99 | }
100 |
101 | private static String getUTCTime() {
102 | @SuppressLint("SimpleDateFormat") SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-d'T'HH:mm:ss'Z'");
103 | sdf.setTimeZone(new SimpleTimeZone(SimpleTimeZone.UTC_TIME, "UTC"));
104 | Calendar cal = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
105 | return sdf.format(cal.getTime());
106 | }
107 |
108 | public static String simpleSoapFormatter(String unformattedStr) {
109 | return unformattedStr.replace("><", ">\n<");
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/app/src/main/java/tk/giesecke/cctvview/findSPMbyMDNS.java:
--------------------------------------------------------------------------------
1 | package tk.giesecke.cctvview;
2 |
3 | import android.app.Service;
4 | import android.content.Intent;
5 | import android.os.CountDownTimer;
6 | import android.os.IBinder;
7 | import android.util.Log;
8 |
9 | import com.github.druk.rxdnssd.BonjourService;
10 | import com.github.druk.rxdnssd.RxDnssd;
11 | import com.github.druk.rxdnssd.RxDnssdBindable;
12 |
13 | import java.net.InetAddress;
14 | import java.util.Map;
15 |
16 | import rx.Subscription;
17 | import rx.android.schedulers.AndroidSchedulers;
18 | import rx.functions.Action1;
19 | import rx.schedulers.Schedulers;
20 |
21 | import static tk.giesecke.cctvview.CCTVview.DEBUG_LOG_TAG;
22 | import static tk.giesecke.cctvview.CCTVview.spmIP;
23 |
24 | public class findSPMbyMDNS extends Service {
25 |
26 | /** Service type for Arduino devices */
27 | private static final String SERVICE_TYPE = "_arduino._tcp.";
28 | /* Available services:
29 | _workstation
30 | _UnoWiFi
31 | _udisks-ssh
32 | _airplay
33 | _raop
34 | _xbmc-events
35 | _xbmc-jsonrpc
36 | _xbmc-jsonrpc-h
37 | _http
38 | _sftp-ssh
39 | _ssh
40 | _arduino
41 | */
42 |
43 | /** Countdown timer to stop the discovery after some time */
44 | private CountDownTimer timer = null;
45 |
46 | /** RxDnssd bindable */
47 | private RxDnssd rxDnssd;
48 | /** Service browser subscription */
49 | private Subscription browseSubscription;
50 |
51 | @Override
52 | public IBinder onBind(Intent intent) {
53 | // TODO: Return the communication channel to the service.
54 | throw new UnsupportedOperationException("Not yet implemented");
55 | }
56 |
57 | @Override
58 | public int onStartCommand( Intent intent, int flags, int startId ) {
59 |
60 | // Start service discovery if not running already
61 | rxDnssd = new RxDnssdBindable(this.getApplicationContext());
62 | if (browseSubscription == null) {
63 | startBrowse();
64 | }
65 |
66 | // Start a countdown to stop the service after 15 seconds
67 | if (timer != null) {
68 | timer.cancel();
69 | timer = null;
70 | }
71 | timer = new CountDownTimer(15000, 1000) {
72 | public void onTick(long millisUntilFinished) {
73 | //Nothing here!
74 | }
75 |
76 | public void onFinish() {
77 | stopBrowse();
78 | timer.cancel();
79 | timer = null;
80 | if (BuildConfig.DEBUG) Log.d(DEBUG_LOG_TAG, "RxDns Discovery finished!");
81 | stopSelf();
82 | }
83 | };
84 | timer.start();
85 |
86 | return super.onStartCommand( intent, flags, startId );
87 | }
88 |
89 | /**
90 | * Start service discovery
91 | *
92 | */
93 | private void startBrowse() {
94 | String discoverService;
95 | if (BuildConfig.DEBUG) Log.d(DEBUG_LOG_TAG, "RxDns Start discovery services for " + SERVICE_TYPE);
96 | discoverService = SERVICE_TYPE;
97 |
98 | browseSubscription = rxDnssd.browse(discoverService, "local.")
99 | .compose(rxDnssd.resolve())
100 | .compose(rxDnssd.queryRecords())
101 | .subscribeOn(Schedulers.io())
102 | .observeOn(AndroidSchedulers.mainThread())
103 | .subscribe(new Action1() {
104 | @Override
105 | public void call(BonjourService bonjourService) {
106 | if (bonjourService.isLost()) {
107 | if (BuildConfig.DEBUG) Log.d(DEBUG_LOG_TAG, "RxDns - lost: " + bonjourService.toString());
108 | }
109 | else {
110 | if (BuildConfig.DEBUG) Log.d(DEBUG_LOG_TAG, "RxDns - found: " + bonjourService.toString());
111 | InetAddress address = bonjourService.getInet4Address();
112 | if (address == null) {
113 | return; // No IPv4 address found for this service
114 | }
115 | Map txtRecord = bonjourService.getTxtRecords();
116 | if (BuildConfig.DEBUG) Log.d(DEBUG_LOG_TAG, "RxDns - txtRecord: " + txtRecord.toString());
117 | if (txtRecord.containsKey("service") && txtRecord.get("service").equalsIgnoreCase("MHC")) {
118 | if (txtRecord.containsKey("type")) {
119 | if (txtRecord.get("type").equalsIgnoreCase("Solar Panel Monitor")) {
120 | spmIP = address.toString().substring(1);
121 | if (BuildConfig.DEBUG) Log.d(DEBUG_LOG_TAG, "RxDns Found SPM!");
122 | // timer.cancel();
123 | stopBrowse();
124 | timer.cancel();
125 | timer = null;
126 | if (BuildConfig.DEBUG) Log.d(DEBUG_LOG_TAG, "RxDns Discovery finished!");
127 | stopSelf();
128 | }
129 | }
130 | }
131 | }
132 | }
133 | }, new Action1() {
134 | @Override
135 | public void call(Throwable throwable) {
136 | if (BuildConfig.DEBUG) Log.d(DEBUG_LOG_TAG, "RxDns - error", throwable);
137 | }
138 | });
139 | }
140 |
141 | /**
142 | * Stop service discovery
143 | *
144 | */
145 | private void stopBrowse() {
146 | if (BuildConfig.DEBUG) Log.d(DEBUG_LOG_TAG, "RxDns - Stop browsing");
147 | browseSubscription.unsubscribe();
148 | browseSubscription = null;
149 | }
150 | }
151 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # Attempt to set APP_HOME
46 | # Resolve links: $0 may be a link
47 | PRG="$0"
48 | # Need this for relative symlinks.
49 | while [ -h "$PRG" ] ; do
50 | ls=`ls -ld "$PRG"`
51 | link=`expr "$ls" : '.*-> \(.*\)$'`
52 | if expr "$link" : '/.*' > /dev/null; then
53 | PRG="$link"
54 | else
55 | PRG=`dirname "$PRG"`"/$link"
56 | fi
57 | done
58 | SAVED="`pwd`"
59 | cd "`dirname \"$PRG\"`/" >/dev/null
60 | APP_HOME="`pwd -P`"
61 | cd "$SAVED" >/dev/null
62 |
63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
64 |
65 | # Determine the Java command to use to start the JVM.
66 | if [ -n "$JAVA_HOME" ] ; then
67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
68 | # IBM's JDK on AIX uses strange locations for the executables
69 | JAVACMD="$JAVA_HOME/jre/sh/java"
70 | else
71 | JAVACMD="$JAVA_HOME/bin/java"
72 | fi
73 | if [ ! -x "$JAVACMD" ] ; then
74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
75 |
76 | Please set the JAVA_HOME variable in your environment to match the
77 | location of your Java installation."
78 | fi
79 | else
80 | JAVACMD="java"
81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
82 |
83 | Please set the JAVA_HOME variable in your environment to match the
84 | location of your Java installation."
85 | fi
86 |
87 | # Increase the maximum file descriptors if we can.
88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
89 | MAX_FD_LIMIT=`ulimit -H -n`
90 | if [ $? -eq 0 ] ; then
91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
92 | MAX_FD="$MAX_FD_LIMIT"
93 | fi
94 | ulimit -n $MAX_FD
95 | if [ $? -ne 0 ] ; then
96 | warn "Could not set maximum file descriptor limit: $MAX_FD"
97 | fi
98 | else
99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
100 | fi
101 | fi
102 |
103 | # For Darwin, add options to specify how the application appears in the dock
104 | if $darwin; then
105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
106 | fi
107 |
108 | # For Cygwin, switch paths to Windows format before running java
109 | if $cygwin ; then
110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
112 | JAVACMD=`cygpath --unix "$JAVACMD"`
113 |
114 | # We build the pattern for arguments to be converted via cygpath
115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
116 | SEP=""
117 | for dir in $ROOTDIRSRAW ; do
118 | ROOTDIRS="$ROOTDIRS$SEP$dir"
119 | SEP="|"
120 | done
121 | OURCYGPATTERN="(^($ROOTDIRS))"
122 | # Add a user-defined pattern to the cygpath arguments
123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
125 | fi
126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
127 | i=0
128 | for arg in "$@" ; do
129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
131 |
132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
134 | else
135 | eval `echo args$i`="\"$arg\""
136 | fi
137 | i=$((i+1))
138 | done
139 | case $i in
140 | (0) set -- ;;
141 | (1) set -- "$args0" ;;
142 | (2) set -- "$args0" "$args1" ;;
143 | (3) set -- "$args0" "$args1" "$args2" ;;
144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
150 | esac
151 | fi
152 |
153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
154 | function splitJvmOpts() {
155 | JVM_OPTS=("$@")
156 | }
157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
159 |
160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
161 |
--------------------------------------------------------------------------------
/app/src/main/java/tk/giesecke/cctvview/OnvifSettings.java:
--------------------------------------------------------------------------------
1 | package tk.giesecke.cctvview;
2 |
3 | import android.annotation.SuppressLint;
4 | import android.content.Intent;
5 | import android.content.SharedPreferences;
6 | import android.os.Bundle;
7 | import android.preference.PreferenceManager;
8 | import android.support.v7.app.ActionBar;
9 | import android.support.v7.app.AppCompatActivity;
10 | import android.support.v7.widget.Toolbar;
11 | import android.util.Log;
12 | import android.view.Menu;
13 | import android.view.MenuItem;
14 | import android.view.View;
15 | import android.view.WindowManager;
16 | import android.widget.EditText;
17 |
18 | import tk.giesecke.cctvview.BuildConfig;
19 |
20 | import static tk.giesecke.cctvview.CCTVview.DEBUG_LOG_TAG;
21 | import static tk.giesecke.cctvview.CCTVview.sharedPref;
22 |
23 | public class OnvifSettings extends AppCompatActivity
24 | implements View.OnClickListener {
25 |
26 | static final String PREF_IP = "url_txt";
27 | static final String PREF_RTSP_PATH = "rtsp_path_txt";
28 | static final String PREF_RTSP_PORT = "rtsp_port_txt";
29 | static final String PREF_WEB_PATH = "web_path_txt";
30 | static final String PREF_WEB_PORT = "web_port_txt";
31 | static final String PREF_USER = "user_txt";
32 | static final String PREF_PWD = "pwd_txt";
33 |
34 | @Override
35 | protected void onCreate(Bundle savedInstanceState) {
36 | super.onCreate(savedInstanceState);
37 | setContentView(R.layout.onvif_settings);
38 | Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
39 | setSupportActionBar(toolbar);
40 | toolbar.setTitle(R.string.title_activity_onvif_settings);
41 | getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN);
42 |
43 | ActionBar discoveryActionBar = getSupportActionBar();
44 | if (discoveryActionBar != null) {
45 | discoveryActionBar.setDisplayHomeAsUpEnabled(true);
46 | } else {
47 | // SOMETHING REALLY BAD HAPPENED !!!!!!!!!!!!!!!!!!!
48 | finish();
49 | }
50 | }
51 |
52 | @Override
53 | protected void onResume() {
54 | super.onResume();
55 | // Fill fields with stored or default data
56 | EditText putField = (EditText) findViewById(R.id.et_cam_ip);
57 | putField.setText(sharedPref.getString(OnvifSettings.PREF_IP,getString(R.string.pref_default_cam_ip)));
58 |
59 | putField = (EditText) findViewById(R.id.et_cam_rtsppath);
60 | putField.setText(sharedPref.getString(OnvifSettings.PREF_RTSP_PATH,getString(R.string.pref_default_rtsp_path)));
61 |
62 | putField = (EditText) findViewById(R.id.et_cam_rtspport);
63 | putField.setText(sharedPref.getString(OnvifSettings.PREF_RTSP_PORT,getString(R.string.pref_default_rtsp_port)));
64 |
65 | putField = (EditText) findViewById(R.id.et_cam_webpath);
66 | putField.setText(sharedPref.getString(OnvifSettings.PREF_WEB_PATH,getString(R.string.pref_default_web_path)));
67 |
68 | putField = (EditText) findViewById(R.id.et_cam_webport);
69 | putField.setText(sharedPref.getString(OnvifSettings.PREF_WEB_PORT,getString(R.string.pref_default_web_port)));
70 |
71 | putField = (EditText) findViewById(R.id.et_cam_user);
72 | putField.setText(sharedPref.getString(OnvifSettings.PREF_USER,getString(R.string.pref_default_username)));
73 |
74 | putField = (EditText) findViewById(R.id.et_cam_pw);
75 | putField.setText(sharedPref.getString(OnvifSettings.PREF_PWD,getString(R.string.pref_default_passwd)));
76 | }
77 |
78 | @Override
79 | public void onClick(View view) {
80 |
81 | }
82 |
83 | @Override
84 | public boolean onCreateOptionsMenu(Menu menu) {
85 | if (BuildConfig.DEBUG) Log.d(DEBUG_LOG_TAG, "onCreateOptionsMenu started");
86 | // Inflate the menu; this adds items to the action bar if it is present.
87 | getMenuInflater().inflate(R.menu.onvif_settings_menu, menu);
88 | return true;
89 | }
90 |
91 | @SuppressLint("ApplySharedPref")
92 | @Override
93 | public boolean onOptionsItemSelected(MenuItem item) {
94 |
95 | switch (item.getItemId()) {
96 | case R.id.action_discover:
97 | Intent myIntent = new Intent(this, OnvifDiscovery.class);
98 | startActivity(myIntent);
99 | break;
100 | case R.id.action_save:
101 | saveSettings();
102 | break;
103 | case android.R.id.home:
104 | saveSettings();
105 | case R.id.action_cancel:
106 | startActivity(new Intent(this, CCTVview.class));
107 | finish();
108 | break;
109 | case R.id.action_clear:
110 | SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(this);
111 | sharedPref.edit().clear().apply();
112 | recreate();
113 | break;
114 | }
115 | return super.onOptionsItemSelected(item);
116 | }
117 |
118 | @SuppressLint("ApplySharedPref")
119 | private void saveSettings() {
120 | SharedPreferences.Editor sharedPrefEditor = sharedPref.edit();
121 |
122 | EditText getField = (EditText) findViewById(R.id.et_cam_ip);
123 | sharedPrefEditor.putString(OnvifSettings.PREF_IP, getField.getText().toString());
124 |
125 | getField = (EditText) findViewById(R.id.et_cam_rtsppath);
126 | sharedPrefEditor.putString(OnvifSettings.PREF_RTSP_PATH, getField.getText().toString());
127 |
128 | getField = (EditText) findViewById(R.id.et_cam_rtspport);
129 | sharedPrefEditor.putString(OnvifSettings.PREF_RTSP_PORT, getField.getText().toString());
130 |
131 | getField = (EditText) findViewById(R.id.et_cam_webpath);
132 | sharedPrefEditor.putString(OnvifSettings.PREF_WEB_PATH, getField.getText().toString());
133 |
134 | getField = (EditText) findViewById(R.id.et_cam_webport);
135 | sharedPrefEditor.putString(OnvifSettings.PREF_WEB_PORT, getField.getText().toString());
136 |
137 | getField = (EditText) findViewById(R.id.et_cam_user);
138 | sharedPrefEditor.putString(OnvifSettings.PREF_USER, getField.getText().toString());
139 |
140 | getField = (EditText) findViewById(R.id.et_cam_pw);
141 | sharedPrefEditor.putString(OnvifSettings.PREF_PWD, getField.getText().toString());
142 |
143 | sharedPrefEditor.commit();
144 | }
145 | }
146 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/onvif_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
16 |
17 |
23 |
24 |
25 |
26 |
33 |
34 |
40 |
41 |
49 |
50 |
61 |
62 |
70 |
71 |
82 |
83 |
91 |
92 |
103 |
104 |
112 |
113 |
124 |
125 |
133 |
134 |
145 |
146 |
154 |
155 |
166 |
167 |
175 |
176 |
187 |
188 |
189 |
--------------------------------------------------------------------------------
/app/src/main/java/tk/giesecke/cctvview/Onvif/DeviceDiscovery.java:
--------------------------------------------------------------------------------
1 | package tk.giesecke.cctvview.Onvif;
2 |
3 | import android.util.Log;
4 |
5 | import java.net.DatagramPacket;
6 | import java.net.DatagramSocket;
7 | import java.net.Inet4Address;
8 | import java.net.InetAddress;
9 | import java.net.InterfaceAddress;
10 | import java.net.MalformedURLException;
11 | import java.net.NetworkInterface;
12 | import java.net.SocketException;
13 | import java.net.SocketTimeoutException;
14 | import java.net.URL;
15 | import java.security.SecureRandom;
16 | import java.util.ArrayList;
17 | import java.util.Arrays;
18 | import java.util.Collection;
19 | import java.util.Comparator;
20 | import java.util.Enumeration;
21 | import java.util.List;
22 | import java.util.Random;
23 | import java.util.TreeSet;
24 | import java.util.UUID;
25 | import java.util.concurrent.ConcurrentSkipListSet;
26 | import java.util.concurrent.CountDownLatch;
27 | import java.util.concurrent.ExecutorService;
28 | import java.util.concurrent.Executors;
29 | import java.util.concurrent.TimeUnit;
30 |
31 | import tk.giesecke.cctvview.BuildConfig;
32 |
33 | /**
34 | Device discovery class to list local accessible devices probed per UDP probe messages.
35 | @author th
36 | @date 2015-06-18
37 | @version 0.1
38 | */
39 | public class DeviceDiscovery {
40 | private static final int WS_DISCOVERY_TIMEOUT = 4000;
41 | private static final int WS_DISCOVERY_PORT = 3702;
42 | private static final String WS_DISCOVERY_ADDRESS_IPv4 = "239.255.255.250";
43 | /**
44 | * Not supported yet.
45 | */
46 | private static final String WS_DISCOVERY_ADDRESS_IPv6 = "[FF02::C]";
47 | public static String WS_DISCOVERY_PROBE_MESSAGE = "http://schemas.xmlsoap.org/ws/2005/04/discovery/Probeurn:uuid:c032cfdd-c3ca-49dc-820e-ee6696ad63e2urn:schemas-xmlsoap-org:ws:2005:04:discovery";
48 | private static final Random random = new SecureRandom();
49 |
50 | // public static void main(String[] args) throws InterruptedException {
51 | // for (URL url : discoverWsDevicesAsUrls()) {
52 | // if (BuildConfig.DEBUG) Log.d("OnvifTest", "DeviceDiscovery - Device discovered: " + url.toString());
53 | // }
54 | // }
55 | //
56 | /**
57 | Discover WS device on the local network and returns Urls
58 | @return list of unique device urls
59 | */
60 | public static Collection discoverWsDevicesAsUrls() {return discoverWsDevicesAsUrls("", "");}
61 |
62 | /**
63 | Discover WS device on the local network with specified filter
64 | @param regexpProtocol url protocol matching regexp like "^http$", might be empty ""
65 | @param regexpPath url path matching regexp like "onvif", might be empty ""
66 | @return list of unique device urls filtered
67 | */
68 | @SuppressWarnings("SameParameterValue")
69 | private static Collection discoverWsDevicesAsUrls(String regexpProtocol, String regexpPath) {
70 | final Collection urls = new TreeSet<>(new Comparator() {
71 | public int compare(URL o1, URL o2) {
72 | return o1.toString().compareTo(o2.toString());
73 | }
74 | });
75 |
76 | for (String key : discoverWsDevices()) {
77 | try {
78 | final URL url = new URL(key);
79 | boolean ok = true;
80 | if (regexpProtocol.length() > 0 && !url.getProtocol().matches(regexpProtocol))
81 | ok = false;
82 | if (regexpPath.length() > 0 && !url.getPath().matches(regexpPath))
83 | ok = false;
84 | if (ok) urls.add(url);
85 | } catch (MalformedURLException e) {
86 | if (BuildConfig.DEBUG) Log.d("OnvifTest", "DeviceDiscovery - MalformedURLException: " + e.toString());
87 | }
88 | }
89 | return urls;
90 | }
91 |
92 | /**
93 | Discover WS device on the local network
94 | @return list of unique devices access strings which might be URLs in most cases
95 | */
96 | private static Collection discoverWsDevices() {
97 | final Collection addresses = new ConcurrentSkipListSet<>();
98 | final CountDownLatch serverStarted = new CountDownLatch(1);
99 | final CountDownLatch serverFinished = new CountDownLatch(1);
100 | final Collection addressList = new ArrayList<>();
101 | try {
102 | final Enumeration interfaces = NetworkInterface.getNetworkInterfaces();
103 | if(interfaces != null) {
104 | while (interfaces.hasMoreElements()) {
105 | NetworkInterface anInterface = interfaces.nextElement();
106 | if( ! anInterface.isLoopback() ) {
107 | final List interfaceAddresses = anInterface.getInterfaceAddresses();
108 | for (InterfaceAddress address : interfaceAddresses) {
109 | addressList.add(address.getAddress());
110 | }
111 | }
112 | }
113 | }
114 | } catch (SocketException e) {
115 | if (BuildConfig.DEBUG) Log.d("OnvifTest", "DeviceDiscovery - SocketException: " + e.toString());
116 | }
117 | ExecutorService executorService = Executors.newCachedThreadPool();
118 | for (final InetAddress address : addressList) {
119 | Runnable runnable = new Runnable() {
120 | public void run() {
121 | try {
122 | final String uuid = UUID.randomUUID().toString();
123 | final String probe = WS_DISCOVERY_PROBE_MESSAGE.replaceAll("urn:uuid:.*", "urn:uuid:" + uuid + "");
124 | final int port = random.nextInt(20000) + 40000;
125 | @SuppressWarnings("SocketOpenedButNotSafelyClosed")
126 | final DatagramSocket server = new DatagramSocket(port, address);
127 | new Thread() {
128 | public void run() {
129 | try {
130 | final DatagramPacket packet = new DatagramPacket(new byte[4096], 4096);
131 | server.setSoTimeout(WS_DISCOVERY_TIMEOUT);
132 | long timerStarted = System.currentTimeMillis();
133 | while (System.currentTimeMillis() - timerStarted < (WS_DISCOVERY_TIMEOUT)) {
134 | serverStarted.countDown();
135 | server.receive(packet);
136 | final Collection collection = parseSoapResponseForUrls(
137 | Arrays.copyOf(packet.getData(), packet.getLength()));
138 | for (String key : collection) {
139 | addresses.add(key);
140 | }
141 | }
142 | } catch (SocketTimeoutException ignored) {
143 | } catch (Exception e) {
144 | if (BuildConfig.DEBUG) Log.d("OnvifTest", "DeviceDiscovery - Exception: " + e.toString());
145 | } finally {
146 | serverFinished.countDown();
147 | server.close();
148 | }
149 | }
150 | }.start();
151 | try {
152 | serverStarted.await(1000, TimeUnit.MILLISECONDS);
153 | } catch (InterruptedException e) {
154 | if (BuildConfig.DEBUG) Log.d("OnvifTest", "DeviceDiscovery - InterruptedException: " + e.toString());
155 | }
156 | if (address instanceof Inet4Address) {
157 | server.send(new DatagramPacket(probe.getBytes(), probe.length(), InetAddress.getByName(WS_DISCOVERY_ADDRESS_IPv4), WS_DISCOVERY_PORT));
158 | } else {
159 | server.send(new DatagramPacket(probe.getBytes(), probe.length(), InetAddress.getByName(WS_DISCOVERY_ADDRESS_IPv6), WS_DISCOVERY_PORT));
160 | }
161 | } catch (Exception e) {
162 | if (BuildConfig.DEBUG) Log.d("OnvifTest", "DeviceDiscovery - Exception: " + e.toString());
163 | }
164 | try {
165 | serverFinished.await((WS_DISCOVERY_TIMEOUT), TimeUnit.MILLISECONDS);
166 | } catch (InterruptedException e) {
167 | if (BuildConfig.DEBUG) Log.d("OnvifTest", "DeviceDiscovery - InterruptedException: " + e.toString());
168 | }
169 | }
170 | };
171 | executorService.submit(runnable);
172 | }
173 | try {
174 | executorService.shutdown();
175 | executorService.awaitTermination(WS_DISCOVERY_TIMEOUT + 2000, TimeUnit.MILLISECONDS);
176 | } catch (InterruptedException e) {
177 | if (BuildConfig.DEBUG) Log.d("OnvifTest", "DeviceDiscovery - InterruptedException: " + e.toString());
178 | }
179 | return addresses;
180 | }
181 |
182 | private static Collection parseSoapResponseForUrls(byte[] data) {
183 | String searchString = new String(data);
184 |
185 | final Collection urls = new ArrayList<>();
186 |
187 | // Get web service address
188 | int startIndex = searchString.indexOf("XAddrs>");
189 | int endIndex;
190 | if (startIndex != -1) {
191 | startIndex += 7;
192 | endIndex = searchString.indexOf("",startIndex);
193 | if (endIndex != -1) {
194 | urls.add(searchString.substring(startIndex,endIndex));
195 | }
196 | }
197 | return urls;
198 | }
199 | }
200 |
--------------------------------------------------------------------------------
/app/src/main/java/tk/giesecke/cctvview/Onvif/OnvifMediaProfiles.java:
--------------------------------------------------------------------------------
1 | package tk.giesecke.cctvview.Onvif;
2 |
3 | public class OnvifMediaProfiles {
4 |
5 | private String profileToken = "unknown";
6 | private String profileName = "unknown";
7 |
8 | public String videoSourceConfigToken = "unknown";
9 | private String videoSourceConfigName = "unknown";
10 | private String videoSourceToken = "unknown";
11 | public int videoSourceHeight = 0;
12 | public int videoSourceWidth = 0;
13 | private int videoSourceY = 0;
14 | private int videoSourceX = 0;
15 |
16 | private String audioSourceConfigToken = "unknown";
17 | private String audioSourceConfigName = "unknown";
18 | private String audioSourceToken = "unknown";
19 |
20 | private String videoEncoderConfigToken = "unknown";
21 | private String videoEncoderConfigName = "unknown";
22 | private String videoEncoderEndcoding = "unknown";
23 | public int videoEncoderWidth = 0;
24 | public int videoEncoderHeight = 0;
25 | private int videoEncoderQuality = 0;
26 | private int videoEncoderFrameRateLimit = 0;
27 | private int videoEncoderFrameRateInterval = 0;
28 | private int videoEncoderBitrateLimit = 0;
29 | private String videoEncoderMulticastAddr = "unknown";
30 | private int videoEncoderMulticastPort = 0;
31 | private int videoEncoderMulticastTTL = 0;
32 | private boolean videoEncoderMulticastAutoStart = false;
33 | private String videoEncoderSessionTimeout = "unknown";
34 |
35 | private String audioEncoderConfigToken = "unknown";
36 | private String audioEncoderConfigName = "unknown";
37 | private String audioEncoderEndcoding = "unknown";
38 | private int audioEncoderBitrate = 0;
39 | private int audioEncoderSampleRate = 0;
40 | private String audioEncoderMulticastAddr = "unknown";
41 | private int audioEncoderMulticastPort = 0;
42 | private int audioEncoderMulticastTTL = 0;
43 | private boolean audioEncoderMulticastAutoStart = false;
44 | private String audioEncoderSessionTimeout = "unknown";
45 |
46 | public String ptzConfigToken = "unknown";
47 | private String ptzConfigName = "unknown";
48 | public String ptzNodeToken = "unknown";
49 |
50 | @SuppressWarnings("SameReturnValue")
51 | public static String getProfilesCommand() {
52 | return "";
53 | }
54 |
55 | public static boolean parseProfilesResponse(String response, OnvifMediaProfiles parsed) {
56 | try {
57 | OnvifResponseParser.lastIndex = 0; // Start from beginning of response
58 | parsed.profileToken = OnvifResponseParser.parseOnvifString("Profiles token=\"", "\">", response);
59 | parsed.profileName = OnvifResponseParser.parseOnvifString("Name>", "", response);
61 | parsed.videoSourceConfigName = OnvifResponseParser.parseOnvifString("Name>", "", "", response);
68 | parsed.audioSourceConfigName = OnvifResponseParser.parseOnvifString("Name>", "", "", response);
71 | parsed.videoEncoderConfigName = OnvifResponseParser.parseOnvifString("Name>", "", "", "", "", "", "", "", "", "", "", "", "", response);
85 | parsed.audioEncoderConfigName = OnvifResponseParser.parseOnvifString("Name>", "", "", "", "", "", "", "", "", response);
95 | parsed.ptzConfigName = OnvifResponseParser.parseOnvifString("Name>", "", " bufferQueue = new LinkedBlockingDeque<>();
33 | private int picWidth,picHeight;
34 | private byte[] header_sps;
35 | private byte[] header_pps;
36 | private boolean isStop;
37 | private final HandlerThread thread;
38 |
39 | @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
40 |
41 | public H264Stream(RtspClient.SDPInfo sp) {
42 | mSDPinfo = sp;
43 | thread = new HandlerThread("H264StreamThread");
44 | thread.start();
45 | mHandler = new Handler(thread.getLooper());
46 | if(sp.SPS != null) decodeSPS();
47 | }
48 |
49 | @SuppressLint("ObsoleteSdkInt")
50 | private void configMediaDecoder(){
51 | if(Build.VERSION.SDK_INT > 15) {
52 | try {
53 | mMeidaCodec = MediaCodec.createDecoderByType("Video/AVC");
54 | MediaFormat mediaFormat = new MediaFormat();
55 | mediaFormat.setString(MediaFormat.KEY_MIME, "Video/AVC");
56 | mediaFormat.setByteBuffer("csd-0", ByteBuffer.wrap(header_sps));
57 | mediaFormat.setByteBuffer("csd-1", ByteBuffer.wrap(header_pps));
58 | mediaFormat.setInteger(MediaFormat.KEY_WIDTH, picWidth);
59 | mediaFormat.setInteger(MediaFormat.KEY_HEIGHT, picHeight);
60 | mMeidaCodec.configure(mediaFormat, mSurfaceView.getHolder().getSurface(), null, 0);
61 |
62 | mMeidaCodec.setVideoScalingMode(MediaCodec.VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING);
63 |
64 | mMeidaCodec.start();
65 | //noinspection deprecation
66 | inputBuffers = mMeidaCodec.getInputBuffers();
67 | isStop = false;
68 | } catch (IOException | IllegalArgumentException e) {
69 | if (BuildConfig.DEBUG) Log.d(CCTVview.DEBUG_LOG_TAG, "H264Stream - configMediaDecoder exception: " + e.getMessage());
70 | e.printStackTrace();
71 | }
72 | }
73 | }
74 |
75 | private void startMediaHardwareDecode() {
76 | mHandler.post(hardwareDecodeThread);
77 | }
78 |
79 | private final Runnable hardwareDecodeThread = new Runnable() {
80 | @Override
81 | public void run() {
82 | int mCount = 0;
83 | int inputBufferIndex,outputBufferIndex;
84 | MediaCodec.BufferInfo info = new MediaCodec.BufferInfo();
85 | byte[] tmpByte;
86 | int framType;
87 | boolean startKeyFrame = false;
88 |
89 | configMediaDecoder();
90 | while (!Thread.interrupted() && !isStop) {
91 | try {
92 | tmpByte = bufferQueue.take();
93 | framType = tmpByte[4]&0x1F;
94 | if(framType == 5) startKeyFrame = true;
95 | if((startKeyFrame || framType == 7 || framType == 8) && mMeidaCodec != null) {
96 | inputBufferIndex = mMeidaCodec.dequeueInputBuffer(2000);
97 | if (inputBufferIndex > 1) {
98 | ByteBuffer inputBuffer = inputBuffers[inputBufferIndex];
99 | inputBuffer.clear();
100 | inputBuffer.put(tmpByte);
101 | mMeidaCodec.queueInputBuffer(inputBufferIndex, 0, tmpByte.length, mCount, 0);
102 | outputBufferIndex = mMeidaCodec.dequeueOutputBuffer(info, 1000);
103 | switch (outputBufferIndex) {
104 | //noinspection deprecation
105 | case MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED:
106 | break;
107 | case MediaCodec.INFO_OUTPUT_FORMAT_CHANGED:
108 | break;
109 | case MediaCodec.INFO_TRY_AGAIN_LATER:
110 | break;
111 | default:
112 | mMeidaCodec.releaseOutputBuffer(outputBufferIndex, true);
113 | break;
114 | }
115 | mCount++;
116 | }
117 | }
118 | } catch (InterruptedException | IllegalStateException e) {
119 | // Log.e(tag,"Wait the buffer come..");
120 | if (BuildConfig.DEBUG) Log.d(CCTVview.DEBUG_LOG_TAG, "H264Stream - hardwareDecodeThread exception: " + e.getMessage());
121 | }
122 | }
123 | bufferQueue.clear();
124 | mMeidaCodec.stop();
125 | mMeidaCodec.release();
126 | mMeidaCodec = null;
127 | }
128 | };
129 |
130 | @SuppressLint("ObsoleteSdkInt")
131 | public void stop(){
132 | bufferQueue.clear();
133 | isStop = true;
134 | try {
135 | Thread.sleep(100);
136 | } catch (InterruptedException e) {
137 | if (BuildConfig.DEBUG) Log.d(CCTVview.DEBUG_LOG_TAG, "H264Stream - stop exception: " + e.getMessage());
138 | }
139 | mHandler.removeCallbacks(hardwareDecodeThread);
140 | if(mMeidaCodec != null) {
141 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
142 | try {
143 | mMeidaCodec.stop();
144 | } catch (IllegalStateException e) {
145 | if (BuildConfig.DEBUG) Log.d(CCTVview.DEBUG_LOG_TAG, "H264Stream - stop exception: " + e.getMessage());
146 | }
147 | mMeidaCodec.release();
148 | }
149 | mMeidaCodec = null;
150 | }
151 | super.stop();
152 | thread.quit();
153 | mSurfaceView.setVisibility(ViewStub.GONE);
154 | mSurfaceView.setVisibility(ViewStub.VISIBLE);
155 | }
156 |
157 | /* This method is used to decode pic width and height from the sps info,
158 | * which got from the RTSP DESCRIPE request, SDP info.
159 | */
160 | private void decodeSPS(){
161 | int i,offset = 32;
162 | int pic_width_len,pic_height_len;
163 | byte[] sps = Base64.decode(mSDPinfo.SPS, Base64.NO_WRAP);
164 | byte[] pps = Base64.decode(mSDPinfo.PPS, Base64.NO_WRAP);
165 | int profile_idc = sps[1];
166 | header_pps = new byte[pps.length];
167 | header_sps = new byte[sps.length];
168 | System.arraycopy(sps,0,header_sps,0,sps.length);
169 | System.arraycopy(pps,0,header_pps,0,pps.length);
170 | offset += getUeLen(sps,offset);//jump seq_parameter_set_id
171 | if(profile_idc == 100 || profile_idc == 110 || profile_idc == 122
172 | || profile_idc == 144) {
173 | int chroma_format_idc = (getUeLen(sps,offset) == 1)?0:
174 | ( sps[(offset+getUeLen(sps,offset))/8] >>
175 | (7-((offset+getUeLen(sps,offset))%8)) );
176 | offset += getUeLen(sps,offset);//jump chroma_format_idc
177 | if(chroma_format_idc == 3)
178 | offset++; //jump residual_colour_transform_flag
179 | offset += getUeLen(sps,offset);//jump bit_depth_luma_minus8
180 | offset += getUeLen(sps,offset);//jump bit_depth_chroma_minus8
181 | offset ++; //jump qpprime_y_zero_transform_bypass_flag
182 | int seq_scaling_matrix_present_flag = (sps[offset/8] >> (8-(offset%8)))&0x01;
183 | if(seq_scaling_matrix_present_flag == 1) offset += 8; //jump seq_scaling_list_present_flag
184 | }
185 | offset += getUeLen(sps,offset);//jump log2_max_frame_num_minus4
186 | int pic_order_cnt_type = (getUeLen(sps,offset) == 1)?0:
187 | ( sps[(offset+getUeLen(sps,offset))/8] >>
188 | (7-((offset+getUeLen(sps,offset))%8)) );
189 | offset += getUeLen(sps,offset);
190 | if(pic_order_cnt_type == 0) {
191 | offset += getUeLen(sps,offset);
192 | }
193 | else if(pic_order_cnt_type == 1) {
194 | offset++; //jump delta_pic_order_always_zero_flag
195 | offset += getUeLen(sps,offset); //jump offset_for_non_ref_pic
196 | offset += getUeLen(sps,offset); //jump offset_for_top_to_bottom_field
197 | int num_ref_frames_inpic_order_cnt_cycle = ( sps[(offset+getUeLen(sps,offset))/8] >>
198 | (7-((offset+getUeLen(sps,offset))%8)) );
199 | for(i=0; i 15) {
263 | startMediaHardwareDecode();
264 | } else {
265 | if (BuildConfig.DEBUG) Log.d(CCTVview.DEBUG_LOG_TAG, "H264Stream - The Platform not support the hardware decode H264!");
266 | }
267 | }
268 |
269 | @Override
270 | protected void decodeH264Stream() {
271 | try {
272 | bufferQueue.put(NALUnit);
273 | } catch (InterruptedException e) {
274 | if (BuildConfig.DEBUG) Log.d(CCTVview.DEBUG_LOG_TAG, "H264Stream - decodeH264Stream exception: " + e.getMessage());
275 | }
276 | }
277 | }
278 |
--------------------------------------------------------------------------------
/app/src/main/java/tk/giesecke/cctvview/Rtsp/Socket/RtpSocket.java:
--------------------------------------------------------------------------------
1 | package tk.giesecke.cctvview.Rtsp.Socket;
2 |
3 | import android.util.Log;
4 |
5 | import tk.giesecke.cctvview.BuildConfig;
6 | import tk.giesecke.cctvview.CCTVview;
7 | import tk.giesecke.cctvview.Rtsp.Stream.RtpStream;
8 |
9 | import java.io.BufferedReader;
10 | import java.io.IOException;
11 | import java.io.InputStream;
12 | import java.io.InputStreamReader;
13 | import java.net.DatagramPacket;
14 | import java.net.DatagramSocket;
15 | import java.net.Socket;
16 | import java.net.SocketException;
17 | import java.util.concurrent.LinkedBlockingDeque;
18 |
19 | /**
20 | *RtpSocket is used to set up the rtp socket and receive the data via udp or tcp protocol
21 | * 1. set up the socket , four different socket : video udp socket, audio udp socket, video tcp socket, audio tcp socket
22 | * 2. make a thread to get the data form rtp server
23 | */
24 | @SuppressWarnings({"AccessStaticViaInstance", "SameParameterValue"})
25 | public class RtpSocket implements Runnable {
26 | private final static String tag = "RtpSocket";
27 | // Unused as audio streams are not implemented!!!!
28 | // private final static int TRACK_VIDEO = 0x01;
29 | // private final static int TRACK_AUDIO = 0x02;
30 |
31 | private DatagramSocket mUdpSocket;
32 | private DatagramPacket mUdpPackets;
33 | private Socket mTcpSocket;
34 | private BufferedReader mTcpPackets;
35 |
36 | private RtcpSocket mRtcpSocket;
37 | private Thread mThread;
38 | private final byte[] message = new byte[2048];
39 | private final int port;
40 | private final String ip;
41 | private final boolean isTcptranslate;
42 | private RtpStream mRtpStream;
43 | private final int serverPort;
44 | private long recordTime = 0;
45 | private boolean useRtspTcpSocket,isStoped;
46 | private InputStream rtspInputStream;
47 | private final LinkedBlockingDeque tcpBuffer = new LinkedBlockingDeque<>();
48 | private Thread tcpThread;
49 |
50 | private static class rtspPacketInfo {
51 | int len;
52 | int offset;
53 | boolean inNextPacket;
54 | public byte[] data;
55 | }
56 | private rtspPacketInfo rtspBuffer = new rtspPacketInfo();
57 | private boolean packetFlag;
58 |
59 | // trackType is unused as audio streams are not implemented!!!!
60 | // public RtpSocket(boolean isTcptranslate, int port, String ip, int serverPort,int trackType) {
61 | public RtpSocket(boolean isTcptranslate, int port, String ip, int serverPort) {
62 | if (BuildConfig.DEBUG) Log.d(CCTVview.DEBUG_LOG_TAG, "RtpSocket");
63 | this.port = port;
64 | this.ip = ip;
65 | this.isTcptranslate = isTcptranslate;
66 | this.serverPort = serverPort;
67 | this.isStoped = false;
68 | if(serverPort == -1) useRtspTcpSocket = false;
69 | else if(serverPort == -2) useRtspTcpSocket = true;
70 | if(!isTcptranslate) setupUdpSocket();
71 | }
72 |
73 | public void setRtspSocket(Socket s) {
74 | try {
75 | rtspInputStream = s.getInputStream();
76 | } catch (IOException e) {
77 | if (BuildConfig.DEBUG) Log.d(CCTVview.DEBUG_LOG_TAG, "setRtspSocket Exception " + e.getMessage());
78 | }
79 | }
80 |
81 | private void setupUdpSocket() {
82 | if (BuildConfig.DEBUG) Log.d(CCTVview.DEBUG_LOG_TAG, "Start to setup the udp socket , the port is: " + port + "....");
83 | try {
84 | mUdpSocket = new DatagramSocket(port);
85 | } catch (SocketException e) {
86 | if (BuildConfig.DEBUG) Log.d(CCTVview.DEBUG_LOG_TAG, "RtpSocket setupUdpSocket exception: " + e.getMessage());
87 | }
88 | mUdpPackets = new DatagramPacket(message,message.length);
89 | mRtcpSocket = new RtcpSocket(port+1,ip,serverPort+1);
90 | mRtcpSocket.start();
91 | }
92 |
93 | private void tcpRecombineThread() {
94 | tcpThread = new Thread(new Runnable() {
95 | @Override
96 | public void run() {
97 | rtspBuffer.inNextPacket = false;
98 | int offset;
99 | while (!Thread.interrupted() && !isStoped) {
100 | try {
101 | byte[] tcpbuffer = tcpBuffer.take();
102 | offset = 0;
103 |
104 | if(rtspBuffer.inNextPacket) {
105 | if(packetFlag) {
106 | rtspBuffer.len = ((tcpbuffer[0]&0xFF)<<8)|(tcpbuffer[1]&0xFF);
107 | rtspBuffer.data = new byte[rtspBuffer.len];
108 | rtspBuffer.offset = 0;
109 | }
110 |
111 | if(rtspBuffer.len > tcpbuffer.length) {
112 | System.arraycopy(tcpbuffer,0,rtspBuffer.data,rtspBuffer.offset,tcpbuffer.length);
113 | rtspBuffer.offset += tcpbuffer.length;
114 | rtspBuffer.len = rtspBuffer.len - tcpbuffer.length;
115 | rtspBuffer.inNextPacket = true;
116 | } else {
117 | System.arraycopy(tcpbuffer, 0, rtspBuffer.data, rtspBuffer.offset, rtspBuffer.len);
118 | mRtpStream.receiveData(rtspBuffer.data, rtspBuffer.data.length);
119 | offset += rtspBuffer.len;
120 | rtspBuffer.inNextPacket = false;
121 | analysisOnePacket(tcpbuffer,offset);
122 | }
123 | }else{
124 | analysisOnePacket(tcpbuffer,0);
125 | }
126 | } catch (InterruptedException e) {
127 | if (BuildConfig.DEBUG) Log.d(CCTVview.DEBUG_LOG_TAG, "tcpRecombineThread - The tcp buffer queue is empty.. " + e.getMessage());
128 | }
129 | }
130 | }
131 | },"TcpPacketRecombineThread");
132 | tcpThread.start();
133 | }
134 |
135 | private void analysisOnePacket(byte[] data, int offset){
136 | int datalen;
137 | int[] tmp = getRtspFrameInfo(data,offset);
138 | byte[] buffer;
139 | datalen = data.length-tmp[0];
140 | while(tmp[1] != -1) {
141 | if(tmp[1] == -2) {
142 | rtspBuffer.inNextPacket = true;
143 | packetFlag = true;
144 | break;
145 | } else packetFlag = false;
146 | if(tmp[1] < datalen) {
147 | //This packet have some rtsp frame
148 | buffer = new byte[tmp[1]];
149 | System.arraycopy(data,tmp[0],buffer,0,tmp[1]);
150 | mRtpStream.receiveData(buffer,buffer.length);
151 | offset = tmp[0] + tmp[1];
152 | tmp = getRtspFrameInfo(data,offset);
153 | datalen = data.length - tmp[0];
154 | rtspBuffer.inNextPacket = false;
155 | } else if(tmp[1] > datalen) {
156 | //This packet have not enough rtsp frame, next packet have some
157 | rtspBuffer.data = new byte[tmp[1]];
158 | rtspBuffer.len = tmp[1] - datalen;
159 | rtspBuffer.offset = datalen;
160 | if(rtspBuffer.offset != 0){
161 | System.arraycopy(data,tmp[0],rtspBuffer.data,0,datalen);
162 | }
163 | rtspBuffer.inNextPacket = true;
164 | break;
165 | } else if(tmp[1] == datalen) {
166 | buffer = new byte[tmp[1]];
167 | System.arraycopy(data,tmp[0], buffer, 0, tmp[1]);
168 | mRtpStream.receiveData(buffer,buffer.length);
169 | rtspBuffer.inNextPacket = false;
170 | break;
171 | }
172 | }
173 | }
174 |
175 | private int[] getRtspFrameInfo(byte[] data,int offset){
176 | int mOffset,length;
177 | boolean haveRtspFrame = false;
178 | for(mOffset = offset; mOffset< data.length-1; ++mOffset){
179 | if(data[mOffset] == 0x24 && data[mOffset+1] == 0x00) {
180 | haveRtspFrame = true;
181 | break;
182 | }
183 | haveRtspFrame = false;
184 | }
185 | if(haveRtspFrame) {
186 | if(mOffset + 3 < data.length) {
187 | length = ((data[mOffset + 2] & 0xFF) << 8) | (data[mOffset + 3] & 0xFF);
188 | mOffset += 4;
189 | } else length = -2; //This time 0x24 0x00 and data length is not in one packet
190 | }
191 | else
192 | length = -1;
193 | return new int[]{mOffset, length};
194 | }
195 |
196 | private void useRtspTcpReading() {
197 | int len;
198 | byte[] buffer = new byte[1024*10];
199 | try {
200 | while((len = rtspInputStream.read(buffer)) != -1) {
201 | byte[] tcpbuffer = new byte[len];
202 | System.arraycopy(buffer,0,tcpbuffer,0,len);
203 | try {
204 | tcpBuffer.put(tcpbuffer);
205 | } catch (InterruptedException e) {
206 | if (BuildConfig.DEBUG) Log.d(CCTVview.DEBUG_LOG_TAG, "RtpSocket useRtspTcpReading - The tcp buffer queue is full.. " + e.getMessage());
207 | }
208 | }
209 | } catch (IOException e) {
210 | if (BuildConfig.DEBUG) Log.d(CCTVview.DEBUG_LOG_TAG, "RtpSocket useRtspTcpReading exception: " + e.getMessage());
211 | }
212 | }
213 |
214 | private void setupTcpSocket() {
215 | Log.d(tag, "Start to setup the tcp socket , the ip is: " + ip + ", the port is: " + port +"....");
216 | try {
217 | mTcpSocket = new Socket(ip,port);
218 | } catch (IOException e) {
219 | if (BuildConfig.DEBUG) Log.d(CCTVview.DEBUG_LOG_TAG, "RtpSocket setupTcpSocket exception: " + e.getMessage());
220 | }
221 | try {
222 | mTcpPackets = new BufferedReader(new InputStreamReader(mTcpSocket.getInputStream()));
223 | } catch (IOException e) {
224 | if (BuildConfig.DEBUG) Log.d(CCTVview.DEBUG_LOG_TAG, "RtpSocket setupTcpSocket exception: " + e.getMessage());
225 | }
226 | if (BuildConfig.DEBUG) Log.d(CCTVview.DEBUG_LOG_TAG, "setupTcpSocket");
227 | }
228 |
229 | public void startRtpSocket() {
230 | if (BuildConfig.DEBUG) Log.d(CCTVview.DEBUG_LOG_TAG, "startRtpSocket");
231 | mThread = new Thread(this,"RTPSocketThread");
232 | mThread.start();
233 | }
234 |
235 | private void startTcpReading(){
236 | String readLine;
237 | try {
238 | while ( (readLine = mTcpPackets.readLine()) != null ) {
239 | if (BuildConfig.DEBUG) Log.d(CCTVview.DEBUG_LOG_TAG, "the tcp read data is: " + readLine);
240 | }
241 | } catch (IOException e) {
242 | if (BuildConfig.DEBUG) Log.d(CCTVview.DEBUG_LOG_TAG, "RtpSocket startTcpReading exception: " + e.getMessage());
243 | }
244 | }
245 |
246 | private void startUdpReading() {
247 | long currentTime;
248 | if (isStoped) {
249 | return;
250 | }
251 | try {
252 | mUdpSocket.receive(mUdpPackets);
253 | } catch (IOException e) {
254 | if (BuildConfig.DEBUG) Log.d(CCTVview.DEBUG_LOG_TAG, "RtpSocket startUdpReading exception: " + e.getMessage());
255 | }
256 | if (mUdpPackets == null) { // Maybe in stopping status
257 | Log.d(CCTVview.DEBUG_LOG_TAG, "RtpSocket startUdpReading mUdpPackets was NULL");
258 | return;
259 | }
260 | byte[] buffer = new byte[mUdpPackets.getLength()];
261 | System.arraycopy(mUdpPackets.getData(), 0, buffer, 0, mUdpPackets.getLength());
262 | //Use Rtp stream thread to decode the receive data
263 | mRtpStream.receiveData(buffer, mUdpPackets.getLength());
264 |
265 | //every 30s send a rtcp packet to server
266 | currentTime = System.currentTimeMillis();
267 | if(currentTime-30000 > recordTime) {
268 | recordTime = currentTime;
269 | mRtcpSocket.sendReceiverReport();
270 | }
271 | }
272 |
273 | public void setStream(RtpStream stream) {
274 | mRtpStream = stream;
275 | }
276 |
277 | @Override
278 | public void run() {
279 | if (BuildConfig.DEBUG) Log.d(CCTVview.DEBUG_LOG_TAG, "RtpSocket start to get rtp data via socket...");
280 | if(isTcptranslate) {
281 | tcpRecombineThread();
282 | if(useRtspTcpSocket) {
283 | useRtspTcpReading();
284 | }
285 | else{
286 | setupTcpSocket();
287 | startTcpReading();
288 | }
289 | } else {
290 | while ( !Thread.interrupted() )
291 | startUdpReading();
292 | }
293 | }
294 |
295 | public void stop(){
296 | isStoped = true;
297 | if(isTcptranslate) {
298 | if(mTcpSocket != null) {
299 | try {
300 | mTcpSocket.close();
301 | } catch (IOException e) {
302 | if (BuildConfig.DEBUG) Log.d(CCTVview.DEBUG_LOG_TAG, "RtpSocket - stop exception: " + e.getMessage());
303 | }
304 | mTcpPackets = null;
305 | }
306 | }
307 | else{
308 | mUdpSocket.close();
309 | mUdpPackets = null;
310 | }
311 | if(mRtcpSocket!=null){
312 | mRtcpSocket.stop();
313 | mRtcpSocket = null;
314 | }
315 | if(mThread!=null) mThread.interrupt();
316 | if(rtspBuffer!=null) rtspBuffer=null;
317 | if(tcpThread!=null) tcpThread.interrupt();
318 | }
319 | }
320 |
--------------------------------------------------------------------------------
/app/src/main/java/tk/giesecke/cctvview/Onvif/OnvifDeviceCapabilities.java:
--------------------------------------------------------------------------------
1 | package tk.giesecke.cctvview.Onvif;
2 |
3 | /**
4 | * Created from https://www.onvif.org/ver10/device/wsdl/devicemgmt.wsdl
5 | *
6 | * GetCapabilities
7 | * Description:
8 | * Any endpoint can ask for the capabilities of a device using the capability exchange request
9 | * response operation. The device shall indicate all its ONVIF compliant capabilities through the
10 | * GetCapabilities command. The capability list includes references to the addresses (XAddr) of
11 | * the service implementing the interface operations in the category. Apart from the addresses,
12 | * the capabilities only reflect optional functions.
13 | *
14 | * Input:
15 | * [GetCapabilities]
16 | * Category - optional, unbounded; [CapabilityCategory]
17 | * List of categories to retrieve capability information on.
18 | * - enum { 'All', 'Analytics', 'Device', 'Events', 'Imaging', 'Media', 'PTZ' }
19 | * Output:
20 | * [GetCapabilitiesResponse]
21 | * Capabilities [Capabilities] - Capability information.
22 | * Analytics - optional; [AnalyticsCapabilities] - Analytics capabilities
23 | * XAddr [anyURI] - Analytics service URI.
24 | * RuleSupport [boolean] - Indicates whether or not rules are supported.
25 | * AnalyticsModuleSupport [boolean] - Indicates whether or not modules are supported.
26 | * Device - optional; [DeviceCapabilities] - Device capabilities
27 | * XAddr [anyURI] - Device service URI.
28 | * Network - optional; [NetworkCapabilities] - Network capabilities.
29 | * IPFilter - optional; [boolean] - Indicates whether or not IP filtering is supported.
30 | * ZeroConfiguration - optional; [boolean] - Indicates whether or not zeroconf is supported.
31 | * IPVersion6 - optional; [boolean] - indicates whether or not IPv6 is supported.
32 | * DynDNS - optional; [boolean] - indicates whether or not is supported.
33 | * Extension - optional; [NetworkCapabilitiesExtension]
34 | * Dot11Configuration - optional; [boolean]
35 | * Extension - optional; [NetworkCapabilitiesExtension2]
36 | * System - optional; [SystemCapabilities] - System capabilities.
37 | * DiscoveryResolve [boolean] - Indicates whether or not WS Discovery resolve requests are supported.
38 | * DiscoveryBye [boolean] - Indicates whether or not WS-Discovery Bye is supported.
39 | * RemoteDiscovery [boolean] - Indicates whether or not remote discovery is supported.
40 | * SystemBackup [boolean] - Indicates whether or not system backup is supported.
41 | * SystemLogging [boolean] - Indicates whether or not system logging is supported.
42 | * FirmwareUpgrade [boolean] - Indicates whether or not firmware upgrade is supported.
43 | * SupportedVersions - unbounded; [OnvifVersion] - Indicates supported ONVIF version(s).
44 | * Major [int] - Major version number.
45 | * Minor [int] - Two digit minor version number. If major version number is less than "16", X.0.1 maps to "01" and X.2.1 maps to "21" where X stands for Major version number. Otherwise, minor number is month of release, such as "06" for June.
46 | * Extension - optional; [SystemCapabilitiesExtension]
47 | * HttpFirmwareUpgrade - optional; [boolean]
48 | * HttpSystemBackup - optional; [boolean]
49 | * HttpSystemLogging - optional; [boolean]
50 | * HttpSupportInformation - optional; [boolean]
51 | * Extension - optional; [SystemCapabilitiesExtension2]
52 | * IO - optional; [IOCapabilities] - I/O capabilities.
53 | * InputConnectors - optional; [int] - Number of input connectors.
54 | * RelayOutputs - optional; [int] - Number of relay outputs.
55 | * Extension - optional; [IOCapabilitiesExtension]
56 | * Auxiliary - optional; [boolean]
57 | * AuxiliaryCommands - optional, unbounded; [AuxiliaryData]
58 | * Extension [IOCapabilitiesExtension2]
59 | * Security - optional; [SecurityCapabilities] - Security capabilities.
60 | * TLS1.1 [boolean] - Indicates whether or not TLS 1.1 is supported.
61 | * TLS1.2 [boolean] - Indicates whether or not TLS 1.2 is supported.
62 | * OnboardKeyGeneration [boolean] - Indicates whether or not onboard key generation is supported.
63 | * AccessPolicyConfig [boolean] - Indicates whether or not access policy configuration is supported.
64 | * X.509Token [boolean] - Indicates whether or not WS-Security X.509 token is supported.
65 | * SAMLToken [boolean] - Indicates whether or not WS-Security SAML token is supported.
66 | * KerberosToken [boolean] - Indicates whether or not WS-Security Kerberos token is supported.
67 | * RELToken [boolean] - Indicates whether or not WS-Security REL token is supported.
68 | * Extension - optional; [SecurityCapabilitiesExtension]
69 | * TLS1.0 [boolean]
70 | * Extension - optional; [SecurityCapabilitiesExtension2]
71 | * Dot1X [boolean]
72 | * SupportedEAPMethod - optional, unbounded; [int] - EAP Methods supported by the device. The int values refer to the IANA EAP Registry.
73 | * RemoteUserHandling [boolean]
74 | * Extension - optional; [DeviceCapabilitiesExtension]
75 | * Events - optional; [EventCapabilities] - Event capabilities
76 | * XAddr [anyURI] - Event service URI.
77 | * WSSubscriptionPolicySupport [boolean] - Indicates whether or not WS Subscription policy is supported.
78 | * WSPullPointSupport [boolean] - Indicates whether or not WS Pull Point is supported.
79 | * WSPausableSubscriptionManagerInterfaceSupport [boolean] - Indicates whether or not WS Pausable Subscription Manager Interface is supported
80 | * Imaging - optional; [ImagingCapabilities] - Imaging capabilities
81 | * XAddr [anyURI] - Imaging service URI.
82 | * Media - optional; [MediaCapabilities] - Media capabilities
83 | * XAddr [anyURI] - Media service URI.
84 | * StreamingCapabilities [RealTimeStreamingCapabilities] - Streaming capabilities.
85 | * RTPMulticast - optional; [boolean] - Indicates whether or not RTP multicast is supported.
86 | * RTP_TCP - optional; [boolean] - Indicates whether or not RTP over TCP is supported.
87 | * RTP_RTSP_TCP - optional; [boolean] - Indicates whether or not RTP/RTSP/TCP is supported.
88 | * Extension - optional; [RealTimeStreamingCapabilitiesExtension]
89 | * Extension - optional; [MediaCapabilitiesExtension]
90 | * ProfileCapabilities [ProfileCapabilities]
91 | * MaximumNumberOfProfiles [int] - Maximum number of profiles.
92 | * PTZ - optional; [PTZCapabilities] - PTZ capabilities
93 | * XAddr [anyURI] - PTZ service URI.
94 | * Extension - optional; [CapabilitiesExtension]
95 | * DeviceIO - optional; [DeviceIOCapabilities]
96 | * XAddr [anyURI]
97 | * VideoSources [int]
98 | * VideoOutputs [int]
99 | * AudioSources [int]
100 | * AudioOutputs [int]
101 | * RelayOutputs [int]
102 | * Display - optional; [DisplayCapabilities]
103 | * XAddr [anyURI]
104 | * FixedLayout [boolean] - Indication that the SetLayout command supports only predefined layouts.
105 | * Recording - optional; [RecordingCapabilities]
106 | * XAddr [anyURI]
107 | * ReceiverSource [boolean]
108 | * MediaProfileSource [boolean]
109 | * DynamicRecordings [boolean]
110 | * DynamicTracks [boolean]
111 | * MaxStringLength [int]
112 | * Search - optional; [SearchCapabilities]
113 | * XAddr [anyURI]
114 | * MetadataSearch [boolean]
115 | * Replay - optional; [ReplayCapabilities]
116 | * XAddr [anyURI] - The address of the replay service.
117 | * Receiver - optional; [ReceiverCapabilities]
118 | * XAddr [anyURI] - The address of the receiver service.
119 | * RTP_Multicast [boolean] - Indicates whether the device can receive RTP multicast streams.
120 | * RTP_TCP [boolean] - Indicates whether the device can receive RTP/TCP streams
121 | * RTP_RTSP_TCP [boolean] - Indicates whether the device can receive RTP/RTSP/TCP streams.
122 | * SupportedReceivers [int] - The maximum number of receivers supported by the device.
123 | * MaximumRTSPURILength [int] - The maximum allowed length for RTSP URIs.
124 | * AnalyticsDevice - optional; [AnalyticsDeviceCapabilities]
125 | * XAddr [anyURI]
126 | * RuleSupport - optional; [boolean] - Obsolete property.
127 | * Extension - optional; [AnalyticsDeviceExtension]
128 | * Extensions - optional; [CapabilitiesExtension2]
129 | */
130 |
131 | public class OnvifDeviceCapabilities {
132 | private String device_Xaddr = "unknown";
133 | private boolean media_RTPMultiCast = false;
134 | private boolean media_RTP_TCP = false;
135 | private boolean media_RTP_RTSP_TCP = false;
136 | private boolean ptz_hasPtz = false;
137 | private int deviceio_videoSources = 0;
138 | private int deviceio_videoOutputs = 0;
139 | private int deviceio_audioSources = 0;
140 | private int deviceio_audioOutputs = 0;
141 | private int deviceio_relayOutputs = 0;
142 |
143 | @SuppressWarnings("SameReturnValue")
144 | public static String getCapabilitiesCommand() {
145 | return "";
146 | }
147 |
148 | public static boolean parseCapabilitiesResponse(String response, OnvifDeviceCapabilities parsed) {
149 | try {
150 | OnvifResponseParser.lastIndex = 0; // Start from beginning of response
151 | parsed.device_Xaddr = OnvifResponseParser.parseOnvifString("XAddr>", "", "", "", "", response);
156 | parsed.deviceio_videoSources = OnvifResponseParser.parseOnvifInt("VideoSources>", "", "", "", "", " 0) {
183 | parsedResult += "Video sources: " + parsed.deviceio_videoSources + "\n";
184 | }
185 | if (parsed.deviceio_videoOutputs > 0) {
186 | parsedResult += "Video outputs: " + parsed.deviceio_videoOutputs + "\n";
187 | }
188 | if (parsed.deviceio_audioSources > 0) {
189 | parsedResult += "Audio sources: " + parsed.deviceio_audioSources + "\n";
190 | }
191 | if (parsed.deviceio_audioOutputs > 0) {
192 | parsedResult += "Audio outputs: " + parsed.deviceio_audioOutputs + "\n";
193 | }
194 | if (parsed.deviceio_relayOutputs > 0) {
195 | parsedResult += "Relay outputs: " + parsed.deviceio_relayOutputs + "\n";
196 | }
197 | return parsedResult;
198 | }
199 | }
200 |
--------------------------------------------------------------------------------
/app/src/main/java/tk/giesecke/cctvview/OnvifDiscovery.java:
--------------------------------------------------------------------------------
1 | package tk.giesecke.cctvview;
2 |
3 | import android.annotation.SuppressLint;
4 | import android.app.Activity;
5 | import android.app.ProgressDialog;
6 | import android.content.BroadcastReceiver;
7 | import android.content.Context;
8 | import android.content.DialogInterface;
9 | import android.content.Intent;
10 | import android.content.IntentFilter;
11 | import android.content.SharedPreferences;
12 | import android.os.AsyncTask;
13 | import android.os.Bundle;
14 | import android.support.design.widget.Snackbar;
15 | import android.support.v7.app.ActionBar;
16 | import android.support.v7.app.AlertDialog;
17 | import android.support.v7.app.AppCompatActivity;
18 | import android.support.v7.widget.Toolbar;
19 | import android.util.Log;
20 | import android.view.View;
21 | import android.widget.TextView;
22 |
23 | import java.io.IOException;
24 | import java.net.URL;
25 | import java.util.ArrayList;
26 | import java.util.Collection;
27 | import java.util.List;
28 | import java.util.concurrent.TimeUnit;
29 |
30 | import okhttp3.MediaType;
31 | import okhttp3.OkHttpClient;
32 | import okhttp3.Request;
33 | import okhttp3.RequestBody;
34 | import okhttp3.Response;
35 | import tk.giesecke.cctvview.Onvif.DeviceDiscovery;
36 | import tk.giesecke.cctvview.Onvif.OnvifDevice;
37 | import tk.giesecke.cctvview.Onvif.OnvifDeviceCapabilities;
38 | import tk.giesecke.cctvview.Onvif.OnvifDeviceNetworkInterfaces;
39 | import tk.giesecke.cctvview.Onvif.OnvifDeviceScopes;
40 | import tk.giesecke.cctvview.Onvif.OnvifMediaProfiles;
41 | import tk.giesecke.cctvview.Onvif.OnvifMediaStreamUri;
42 |
43 | import static tk.giesecke.cctvview.Onvif.OnvifDeviceCapabilities.capabilitiesToString;
44 | import static tk.giesecke.cctvview.Onvif.OnvifDeviceCapabilities.getCapabilitiesCommand;
45 | import static tk.giesecke.cctvview.Onvif.OnvifDeviceCapabilities.parseCapabilitiesResponse;
46 | import static tk.giesecke.cctvview.Onvif.OnvifDeviceNetworkInterfaces.getNetInterfacesCommand;
47 | import static tk.giesecke.cctvview.Onvif.OnvifDeviceNetworkInterfaces.parseNetworkInterfacesResponse;
48 | import static tk.giesecke.cctvview.Onvif.OnvifDeviceScopes.getScopesCommand;
49 | import static tk.giesecke.cctvview.Onvif.OnvifDeviceScopes.parseScopesResponse;
50 | import static tk.giesecke.cctvview.Onvif.OnvifDeviceScopes.scopesToString;
51 | import static tk.giesecke.cctvview.Onvif.OnvifHeaderBody.getAuthorizationHeader;
52 | import static tk.giesecke.cctvview.Onvif.OnvifHeaderBody.getEnvelopeEnd;
53 | import static tk.giesecke.cctvview.Onvif.OnvifMediaProfiles.getProfilesCommand;
54 | import static tk.giesecke.cctvview.Onvif.OnvifMediaProfiles.parseProfilesResponse;
55 | import static tk.giesecke.cctvview.Onvif.OnvifMediaProfiles.profilesToString;
56 | import static tk.giesecke.cctvview.Onvif.OnvifMediaStreamUri.getStreamUriCommand;
57 | import static tk.giesecke.cctvview.Onvif.OnvifMediaStreamUri.parseStreamUriResponse;
58 | import static tk.giesecke.cctvview.CCTVview.sharedPref;
59 |
60 | public class OnvifDiscovery extends AppCompatActivity {
61 |
62 | /** Action for broadcast message to main activity */
63 | private static final String ONVIF_DISCOVERY_FINISHED = "O_DISC_FIN";
64 | /** Actions for device information collection */
65 | private static final int GET_SCOPES_ID = 0;
66 | private static final int GET_CAPABILITY_ID = 1;
67 | private static final int GET_PROFILES_ID = 2;
68 | private static final int GET_NETINTERF_ID = 3;
69 | private static final int GET_MEDIASTREAM_ID = 4;
70 |
71 | /** List of found devices */
72 | private List onvifDevices;
73 | private OnvifDevice userSelectedDevice;
74 |
75 | private Intent receiverIntent = null;
76 |
77 | @Override
78 | protected void onCreate(Bundle savedInstanceState) {
79 | super.onCreate(savedInstanceState);
80 | setContentView(R.layout.onvif_discovery);
81 | Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
82 | setSupportActionBar(toolbar);
83 |
84 | ActionBar discoveryActionBar = getSupportActionBar();
85 | if (discoveryActionBar != null) {
86 | discoveryActionBar.setDisplayHomeAsUpEnabled(true);
87 | } else {
88 | // SOMETHING REALLY BAD HAPPENED !!!!!!!!!!!!!!!!!!!
89 | finish();
90 | }
91 | onvifDevices = new ArrayList<>();
92 | }
93 |
94 | @Override
95 | public void onResume() {
96 | super.onResume();
97 | if (receiverIntent == null) {
98 | /* Intent filter for broadcast receiver */
99 | IntentFilter intentFilter = new IntentFilter(ONVIF_DISCOVERY_FINISHED);
100 | //Map the intent filter to the receiver
101 | receiverIntent = registerReceiver(onvifDiscoveryBCreceiver, intentFilter);
102 | }
103 |
104 | new ONVIFsearchDevices(this).execute();
105 | }
106 |
107 | @Override
108 | public void onPause() {
109 | super.onPause();
110 | if (receiverIntent != null) {
111 | unregisterReceiver(onvifDiscoveryBCreceiver);
112 | receiverIntent = null;
113 | }
114 | }
115 |
116 | /**
117 | * Communication in Async Task between Android and Arduino Yun
118 | */
119 | private class ONVIFsearchDevices extends AsyncTask {
120 |
121 | ProgressDialog searchDialog;
122 | final Context context;
123 |
124 | ONVIFsearchDevices(Activity activity) {
125 | this.context = activity;
126 | }
127 |
128 | @Override
129 | protected void onPreExecute() {
130 | super.onPreExecute();
131 | searchDialog = new ProgressDialog(context);
132 | searchDialog.setMessage(context.getString(R.string.dlg_txt_wait));
133 | searchDialog.setCancelable(false);
134 | searchDialog.show();
135 | }
136 |
137 | /**
138 | * Background process of device discovery
139 | *
140 | * @param params
141 | * params[0] = soapRequest as string
142 | */
143 | @Override
144 | protected Boolean doInBackground(String... params) {
145 |
146 | OnvifDevice foundDevice = new OnvifDevice();
147 |
148 | boolean hasDevices = false;
149 |
150 | DeviceDiscovery.WS_DISCOVERY_PROBE_MESSAGE = "" +
151 | "" +
152 | "http://schemas.xmlsoap.org/ws/2005/04/discovery/Probe" +
153 | "uuid:84c80cc6-34b7-4e48-bb52-c484522e641d" +
154 | "" +
155 | "http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous" +
156 | "" +
157 | "urn:schemas-xmlsoap-org:ws:2005:04:discovery" +
158 | "" +
159 | "" +
160 | "" +
161 | "dp0:NetworkVideoDisplay" +
162 | "" +
163 | "" +
164 | "";
165 | // Optional to speed up search limit to http protocol and paths including "onvif"
166 | // final Collection urls = DeviceDiscovery.discoverWsDevicesAsUrls("^http$", ".*onvif.*");
167 | Collection urls = DeviceDiscovery.discoverWsDevicesAsUrls();
168 | if (urls.size() != 0) {
169 | hasDevices = true;
170 | for (URL url : urls) {
171 | foundDevice.discoveredURL = url;
172 | onvifDevices.add(foundDevice);
173 | }
174 | }
175 |
176 | DeviceDiscovery.WS_DISCOVERY_PROBE_MESSAGE = "" +
177 | "" +
178 | "http://schemas.xmlsoap.org/ws/2005/04/discovery/Probe" +
179 | "uuid:33c74915-f359-4ae8-950f-992b24965c2c" +
180 | "\n" +
181 | "http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous" +
182 | "" +
183 | "urn:schemas-xmlsoap-org:ws:2005:04:discovery" +
184 | "" +
185 | "" +
186 | "" +
187 | "dp0:Device" +
188 | "" +
189 | "" +
190 | " urls = DeviceDiscovery.discoverWsDevicesAsUrls("^http$", ".*onvif.*");
193 | urls = DeviceDiscovery.discoverWsDevicesAsUrls();
194 | if (urls.size() != 0) {
195 | hasDevices = true;
196 | for (URL url : urls) {
197 | foundDevice.discoveredURL = url;
198 | onvifDevices.add(foundDevice);
199 | }
200 | }
201 |
202 | DeviceDiscovery.WS_DISCOVERY_PROBE_MESSAGE = "" +
203 | "" +
204 | "http://schemas.xmlsoap.org/ws/2005/04/discovery/Probe" +
205 | "uuid:02d6aa57-8cb4-4ab7-af24-3e9f814b8bd1" +
206 | "" +
207 | "http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous" +
208 | "" +
209 | "urn:schemas-xmlsoap-org:ws:2005:04:discovery" +
210 | "" +
211 | "" +
212 | "" +
213 | "dp0:NetworkVideoTransmitter" +
214 | "" +
215 | "" +
216 | "";
217 | // Optional to speed up search limit to http protocol and paths including "onvif"
218 | // final Collection urls = DeviceDiscovery.discoverWsDevicesAsUrls("^http$", ".*onvif.*");
219 | urls = DeviceDiscovery.discoverWsDevicesAsUrls();
220 | if (urls.size() != 0) {
221 | hasDevices = true;
222 | for (URL url : urls) {
223 | foundDevice.discoveredURL = url;
224 | onvifDevices.add(foundDevice);
225 | }
226 | }
227 |
228 | if (hasDevices) {
229 | for (int i=0; i") + 15);
348 | parseProfilesResponse(part1Profile, deviceUnderInvestigation.mediaProfiles[0]);
349 | part1Profile = receivedSOAP.substring(receivedSOAP.lastIndexOf("") + 15);
351 | parseProfilesResponse(part1Profile, deviceUnderInvestigation.mediaProfiles[1]);
352 | break;
353 | case GET_CAPABILITY_ID:
354 | deviceUnderInvestigation.devCapabilities = new OnvifDeviceCapabilities();
355 | parseCapabilitiesResponse(receivedSOAP, deviceUnderInvestigation.devCapabilities);
356 | break;
357 | case GET_NETINTERF_ID:
358 | deviceUnderInvestigation.devNetInterface = new OnvifDeviceNetworkInterfaces();
359 | parseNetworkInterfacesResponse(receivedSOAP, deviceUnderInvestigation.devNetInterface);
360 | break;
361 | case GET_MEDIASTREAM_ID:
362 | deviceUnderInvestigation.mediaStreamUri = new OnvifMediaStreamUri();
363 | parseStreamUriResponse(receivedSOAP, deviceUnderInvestigation.mediaStreamUri);
364 | break;
365 | }
366 |
367 | }
368 |
369 | private void showDiscoveryFailed(String result) {
370 | Snackbar mySnackbar = Snackbar.make(findViewById(android.R.id.content),
371 | result,
372 | Snackbar.LENGTH_INDEFINITE);
373 | mySnackbar.setAction("OK", CCTVview.mOnClickListener);
374 | View snackbarView = mySnackbar.getView();
375 | TextView tv = (TextView) snackbarView.findViewById(android.support.design.R.id.snackbar_text);
376 | tv.setMaxLines(300);
377 | mySnackbar.show();
378 | }
379 |
380 | private void showDiscoverySelection() {
381 | final String[] discResults = new String[onvifDevices.size()];
382 | for (int i=0; i headers = new HashMap<>();
393 |
394 | static Response parseResponse(BufferedReader input) throws IOException {
395 | Response response = new Response();
396 | String line;
397 | Matcher matcher;
398 | int sdpContentLength = 0;
399 | if( (line = input.readLine()) == null) throw new IOException("Connection lost");
400 | matcher = regexStatus.matcher(line);
401 | if(matcher.find())
402 | response.state = Integer.parseInt(matcher.group(1));
403 | else
404 | while ( (line = input.readLine()) != null ) {
405 | matcher = regexStatus.matcher(line);
406 | if(matcher.find()) {
407 | response.state = Integer.parseInt(matcher.group(1));
408 | break;
409 | }
410 | }
411 | Log.d(tag, "The response state is: "+response.state);
412 |
413 | int foundMediaType = 0;
414 | int sdpHaveReadLength = 0;
415 | boolean sdpStartFlag = false;
416 |
417 | while ( (line = input.readLine()) != null) {
418 | if( line.length() > 3 || Describeflag ) {
419 | Log.d(tag, "The line is: " + line + "...");
420 | matcher = regexHeader.matcher(line);
421 | if (matcher.find())
422 | headers.put(matcher.group(1).toLowerCase(Locale.US), matcher.group(2)); //$ to $
423 |
424 | matcher = regexSDPlength.matcher(line);
425 | if(matcher.find()) {
426 | sdpContentLength = Integer.parseInt(matcher.group(1));
427 | sdpHaveReadLength = 0;
428 | }
429 | //Here is trying to get the SDP information from the describe response
430 | if (Describeflag) {
431 | matcher = regexSDPmediadescript.matcher(line);
432 | if (matcher.find())
433 | if (matcher.group(1).equalsIgnoreCase("audio")) {
434 | foundMediaType = 1;
435 | sdpInfo.audioTrackFlag = true;
436 | } else if (matcher.group(1).equalsIgnoreCase("video")) {
437 | foundMediaType = 2;
438 | sdpInfo.videoTrackFlag = true;
439 | }
440 |
441 | matcher = regexSDPpacketizationMode.matcher(line);
442 | if (matcher.find()) {
443 | sdpInfo.packetizationMode = Integer.parseInt(matcher.group(1));
444 | }
445 |
446 | matcher = regexSDPspspps.matcher(line);
447 | if(matcher.find()) {
448 | sdpInfo.SPS = matcher.group(1);
449 | sdpInfo.PPS = matcher.group(2);
450 | }
451 |
452 | matcher = regexSDPgetTrack1.matcher(line);
453 | if(matcher.find())
454 | if (foundMediaType == 1) sdpInfo.audioTrack = "trackID=" + matcher.group(1);
455 | else if (foundMediaType == 2) sdpInfo.videoTrack = "trackID=" + matcher.group(1);
456 |
457 |
458 | matcher = regexSDPgetTrack2.matcher(line);
459 | if(matcher.find())
460 | if (foundMediaType == 1) sdpInfo.audioTrack = matcher.group(1);
461 | else if (foundMediaType == 2) sdpInfo.videoTrack = matcher.group(1);
462 |
463 |
464 | matcher = regexSDPstartFlag.matcher(line);
465 | if(matcher.find()) sdpStartFlag = true;
466 | if(sdpStartFlag) sdpHaveReadLength += line.getBytes().length + 2;
467 | if((sdpContentLength < sdpHaveReadLength + 2) && (sdpContentLength != 0)) {
468 | Describeflag = false;
469 | Log.d(tag, "The SDP info: "
470 | + (sdpInfo.audioTrackFlag ? "have audio info.. " : "haven't the audio info.. ")
471 | + ";" + (sdpInfo.audioTrackFlag ? (" the audio track is " + sdpInfo.audioTrack) : ""));
472 | Log.d(tag, "The SDP info: "
473 | + (sdpInfo.videoTrackFlag ? "have video info.. " : "haven't the vedio info..")
474 | + (sdpInfo.videoTrackFlag ? (" the video track is " + sdpInfo.videoTrack) : "")
475 | + ";" + (sdpInfo.videoTrackFlag ? (" the video SPS is " + sdpInfo.SPS) : "")
476 | + ";" + (sdpInfo.videoTrackFlag ? (" the video PPS is " + sdpInfo.PPS) : "")
477 | + ";" + (sdpInfo.videoTrackFlag ? (" the video packetization mode is " + sdpInfo.packetizationMode) : ""));
478 | break;
479 | }
480 | }
481 | } else {
482 | break;
483 | }
484 |
485 | }
486 |
487 | if( line == null ) throw new IOException("Connection lost");
488 |
489 | return response;
490 | }
491 | }
492 | }
493 |
--------------------------------------------------------------------------------