Skip to content

Commit

Permalink
Fixed not sending keep-alive packets
Browse files Browse the repository at this point in the history
  • Loading branch information
alexeyvasilyev committed Jan 19, 2021
1 parent acbaf3a commit 8412a1a
Show file tree
Hide file tree
Showing 7 changed files with 256 additions and 48 deletions.
8 changes: 4 additions & 4 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ apply plugin: 'kotlin-android-extensions'

android {
compileSdkVersion 30
buildToolsVersion "30.0.2"
buildToolsVersion "30.0.3"

defaultConfig {
applicationId "com.alexvas.rtsp.demo"
Expand Down Expand Up @@ -46,10 +46,10 @@ dependencies {
implementation 'com.google.android.material:material:1.2.1'
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
implementation "androidx.navigation:navigation-fragment:${androidXNavigationVersion}"
implementation "androidx.navigation:navigation-ui:${androidXNavigationVersion}"
implementation "androidx.navigation:navigation-fragment-ktx:${androidXNavigationVersion}"
implementation "androidx.navigation:navigation-ui-ktx:${androidXNavigationVersion}"
implementation 'com.github.AppDevNext.Logcat:LogcatCore:2.1.1'
implementation "androidx.navigation:navigation-fragment-ktx:${androidXNavigationVersion}"
implementation "androidx.navigation:navigation-ui-ktx:${androidXNavigationVersion}"
implementation 'com.github.AppDevNext.Logcat:LogcatCore:2.3.1'
implementation project(':library-rtsp')
}
11 changes: 8 additions & 3 deletions app/src/main/java/com/alexvas/rtsp/demo/ui/live/LiveFragment.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,7 @@ import android.text.Editable
import android.text.TextWatcher
import android.util.Log
import android.view.*
import android.widget.Button
import android.widget.CheckBox
import android.widget.EditText
import android.widget.*
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import com.alexvas.rtsp.RtspClient
Expand Down Expand Up @@ -43,6 +41,7 @@ class LiveFragment : Fragment(), SurfaceHolder.Callback {
private var surfaceHeight: Int = 1080
private var checkVideo: CheckBox? = null
private var checkAudio: CheckBox? = null
private var textStatus: TextView? = null
private var videoMimeType: String = ""
private var audioMimeType: String = ""
private var audioSampleRate: Int = 0
Expand Down Expand Up @@ -84,16 +83,19 @@ class LiveFragment : Fragment(), SurfaceHolder.Callback {
val listener = object: RtspClient.RtspClientListener {
override fun onRtspDisconnected() {
if (DEBUG) Log.v(TAG, "onRtspDisconnected()")
Handler(Looper.getMainLooper()).post { textStatus?.text = "RTSP disconnected" }
rtspStopped.set(true)
}

override fun onRtspFailed(message: String?) {
Log.e(TAG, "onRtspFailed(message=\"$message\")")
Handler(Looper.getMainLooper()).post { textStatus?.text = "RTSP failed: $message" }
rtspStopped.set(true)
}

override fun onRtspConnected(sdpInfo: RtspClient.SdpInfo) {
if (DEBUG) Log.v(TAG, "onRtspConnected()")
Handler(Looper.getMainLooper()).post { textStatus?.text = "RTSP connected" }
if (sdpInfo.videoTrack != null) {
videoFrameQueue.clear()
when (sdpInfo.videoTrack?.videoCodec) {
Expand Down Expand Up @@ -128,6 +130,7 @@ class LiveFragment : Fragment(), SurfaceHolder.Callback {

override fun onRtspFailedUnauthorized() {
Log.e(TAG, "onRtspFailedUnauthorized()")
Handler(Looper.getMainLooper()).post { textStatus?.text = "RTSP username or password is incorrect" }
rtspStopped.set(true)
}

Expand Down Expand Up @@ -160,6 +163,7 @@ class LiveFragment : Fragment(), SurfaceHolder.Callback {

override fun onRtspConnecting() {
if (DEBUG) Log.v(TAG, "onRtspConnecting()")
Handler(Looper.getMainLooper()).post { textStatus?.text = "RTSP connecting" }
}
}
val uri: Uri = Uri.parse(liveViewModel.rtspRequest.value)
Expand Down Expand Up @@ -203,6 +207,7 @@ class LiveFragment : Fragment(), SurfaceHolder.Callback {
val surfaceView: SurfaceView = root.findViewById(R.id.surfaceView)
checkVideo = root.findViewById(R.id.check_video)
checkAudio = root.findViewById(R.id.check_audio)
textStatus = root.findViewById(R.id.text_status)

surfaceView.holder.addCallback(this)
surface = surfaceView.holder.surface
Expand Down
1 change: 1 addition & 0 deletions app/src/main/res/layout/fragment_live.xml
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@

<TextView
android:id="@+id/text_status"
android:layout_marginTop="10dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>

Expand Down
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
buildscript {
ext.kotlin_version = '1.4.20'
ext.kotlin_version = '1.4.21'
repositories {
jcenter()
google()
Expand Down
4 changes: 2 additions & 2 deletions constants.gradle
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
project.ext {
compileSdkVersion = 30
buildToolsVersion = '30.0.2'
buildToolsVersion = '30.0.3'
minSdkVersion = 21 // 5.0
targetSdkVersion = 30 // 11.0
releaseVersion = "1.2.8"
Expand All @@ -9,5 +9,5 @@ project.ext {
androidXAnnotationVersion = '1.1.0'
androidXAppcompatVersion = '1.1.0'
androidXNavigationVersion = '2.3.2'
exoplayerVersion = '2.12.2'
exoplayerVersion = '2.12.3'
}
67 changes: 60 additions & 7 deletions library-rtsp/src/main/java/com/alexvas/rtsp/RtpParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ class RtpParser {
private static final String TAG = RtpParser.class.getSimpleName();
private static final boolean DEBUG = false;

private static int RTP_HEADER_SIZE = 12;
private final static int RTP_HEADER_SIZE = 12;

public static class RtpHeader {
public int version;
Expand All @@ -29,12 +29,48 @@ public static class RtpHeader {
public long ssrc;
public int payloadSize;

@NonNull
// If RTP header found, return 4 bytes of the header
private static boolean searchForNextRtpHeader(@NonNull InputStream inputStream, @NonNull byte[] header /*out*/) throws IOException {
if (header.length < 4)
throw new IOException("Invalid allocated buffer size");

int bytesRemaining = 100000; // 100 KB max to check
boolean foundFirstByte = false;
boolean foundSecondByte = false;
byte[] oneByte = new byte[1];
// Search for {0x24, 0x00}
do {
if (bytesRemaining-- < 0)
return false;
// Read 1 byte
NetUtils.readData(inputStream, oneByte, 0, 1);
if (foundFirstByte) {
// Found 0x24. Checking for 0x00.
if (oneByte[0] == 0x00)
foundSecondByte = true;
else
foundFirstByte = false;
}
if (!foundFirstByte && oneByte[0] == 0x24) {
// Found 0x24
foundFirstByte = true;
}
} while (!foundSecondByte);
header[0] = 0x24;
header[1] = 0x00;
// Read 2 bytes more (packet size)
NetUtils.readData(inputStream, header, 2, 2);
return true;
}

@Nullable
private static RtpHeader parseData(@NonNull byte[] header, int packetSize) {
RtpHeader rtpHeader = new RtpHeader();
rtpHeader.version = (header[0] & 0xFF) >> 6;
if (rtpHeader.version != 2) {
Log.e("RTP","This is not a RTP packet (" + rtpHeader.version + ")");
if (DEBUG)
Log.e(TAG,"Not a RTP packet (" + rtpHeader.version + ")");
return null;
}

// 80 60 40 91 fd ab d4 2a
Expand All @@ -50,6 +86,13 @@ private static RtpHeader parseData(@NonNull byte[] header, int packetSize) {
return rtpHeader;
}

private static int getPacketSize(@NonNull byte[] header) {
int packetSize = ((header[2] & 0xFF) << 8) | (header[3] & 0xFF);
if (DEBUG)
Log.d(TAG, "Packet size: " + packetSize);
return packetSize;
}

public void dumpHeader() {
Log.d("RTP","RTP header version: " + version
+ ", padding: " + padding
Expand All @@ -74,14 +117,24 @@ static RtpHeader readHeader(@NonNull InputStream inputStream) throws IOException
if (DEBUG && header[0] == 0x24)
Log.d(TAG, header[1] == 0 ? "RTP packet" : "RTCP packet");

int packetSize = ((header[2] & 0xFF) << 8) | (header[3] & 0xFF);
int packetSize = RtpHeader.getPacketSize(header);
if (DEBUG)
Log.d(TAG, "Packet size: " + packetSize);

if (NetUtils.readData(inputStream, header, 0, header.length) == header.length) {
return RtpHeader.parseData(header, packetSize);
} else {
return null;
RtpHeader rtpHeader = RtpHeader.parseData(header, packetSize);
if (rtpHeader == null) {
// Header not found. Possible keep-alive response. Search for another RTP header.
boolean foundHeader = RtpHeader.searchForNextRtpHeader(inputStream, header);
if (foundHeader) {
packetSize = RtpHeader.getPacketSize(header);
if (NetUtils.readData(inputStream, header, 0, header.length) == header.length)
return RtpHeader.parseData(header, packetSize);
}
} else {
return rtpHeader;
}
}
return null;
}
}
Loading

0 comments on commit 8412a1a

Please sign in to comment.