Closed Pitel closed 3 years ago
Hello Pitel,
Thanks for your interest in RTSP, right now ExoPlayer does not support RTSP auth, but it is something that we have planned. Please stay tuned.
If you are looking to implement auth yourself, take a look at RtspClient.MessageListener
. You can customize RtspRequest
's header section with RtspHeaders.Builder
Hi Pitel,
Wondered if you could provide a sample source to help us test?
Hi, sorry, unfortunately I can't. Our testing camera is on the intranet and I'm not aware of any public cams required for testing.
But I can show you the requests (but it should be just the same header as HTTP uses) and reposnses and help you test it.
The requests are useful too, thanks!
You can send us an email at dev.exoplayer@gmail.com using a subject in the format "Issue #8941", if you find it uncomfortable sharing that on Github.
I guess it's alright sharing it there. It's just a testing camera on the intranet with dummy password.
After a bit of debugging, I find you probably has 2 options:
The first one is to simply use the URI with username and password in it, like this (notice the admin:heslo123@
part):
DESCRIBE rtsp://admin:heslo123@10.2.33.226:554/Streaming/Channels/101?transportmode=unicast&profile=Profile_1 RTSP/1.0
CSeq: 2
User-Agent: ExoPlayerLib/2.10.4 (Media Player for Android)
Accept: application/sdp
RTSP/1.0 200 OK
CSeq: 2
Content-Base: rtsp://admin:heslo123@10.2.33.226:554/Streaming/Channels/101/
Content-Type: application/sdp
Content-Length: 901
<< SDP IS HERE >>
This is what I see when I'm using ExoPlayer from #3854.
Another option is what ffmpeg is doing. It sends the plain request, the camera responds with 401 and some WWW-Authenticate
headers which should be used in the following repeated request:
DESCRIBE rtsp://10.2.33.226:554/Streaming/Channels/101?transportmode=unicast&profile=Profile_1 RTSP/1.0
Accept: application/sdp
CSeq: 2
User-Agent: libmpv
RTSP/1.0 401 Unauthorized
CSeq: 2
WWW-Authenticate: Digest realm="2857be52f47f", nonce="f4cba07ad14b5bf181ac77c5a92ba65f", stale="FALSE"
WWW-Authenticate: Basic realm="2857be52f47f"
Date: Tue, Apr 25 1972 20:27:07 GMT
DESCRIBE rtsp://10.2.33.226:554/Streaming/Channels/101?transportmode=unicast&profile=Profile_1 RTSP/1.0
Accept: application/sdp
CSeq: 3
User-Agent: libmpv
Authorization: Digest username="admin", realm="2857be52f47f", nonce="f4cba07ad14b5bf181ac77c5a92ba65f", uri="rtsp://10.2.33.226:554/Streaming/Channels/101?transportmode=unicast&profile=Profile_1", response="50e0c1696fbccefe96b3a8d02379fad9"
RTSP/1.0 200 OK
CSeq: 3
Content-Type: application/sdp
Content-Base: rtsp://10.2.33.226:554/Streaming/Channels/101/
Content-Length: 856
<< SDP IS HERE >>
I'm not the expert here, but I suppose the second option is more secure. However, when testing it, I found out that our camera responds 200 OK
disregarding the nonce
parameter :shrug:.
Hey @claincly. I've seen your commit on dev-v2 on basic and digest auth. So, I tried it with my camera that is located on my local network. It just streams an h.264 stream on rtsp. It correctly parses the WWW-Authenticate
header and then sends DESCRIBE signal again, but then it just stucks.
After some debugging I found out that it stucks in RtspMessageChannel.handleRtspMessageLine
. It just iterates through the InputStream
trying to find \r\n
(CRLF) until it hits EOF. Therefore there is no video stream.
Now, I am not sure if it's "Test Ready" or not, but if there is anything i can provide, please tell.
I also attached logs below
opening message channel on socket Socket[address=/192.168.1.114,port=554,localPort=43808]
OPTIONS rtsp://192.168.1.114:554/videoMain RTSP/1.0
cseq: 0
user-agent: ExoPlayerLib/2.14.0
RTSP/1.0 200 OK
Allow: OPTIONS, DESCRIBE, PLAY, PAUSE, GET_PARAMETER, SET_PARAMETER, TEARDOWN
CSeq: 0
Content-Length: 0
DESCRIBE rtsp://192.168.1.114:554/videoMain RTSP/1.0
cseq: 1
user-agent: ExoPlayerLib/2.14.0
RTSP/1.0 401 Unauthorized
WWW-Authenticate: Digest realm="MyRealm",nonce="90189dc995f6644d"
CSeq: 1
Content-Length: 0
DESCRIBE rtsp://192.168.1.114:554/videoMain RTSP/1.0
cseq: 2
user-agent: ExoPlayerLib/2.14.0
authorization: Digest username="admin", realm="MyRealm", nonce="90189dc995f6644d", uri="rtsp://192.168.1.114:554/videoMain", response="4bc524332a8cc8248cb4dec75b554ebb"
RTSP/1.0 200 OK
Content-Base: rtsp://192.168.1.114:554/videoMain/
Content-Type: application/sdp
CSeq: 2
Content-Length: 303
@k1llrogg Is the log complete?
On a rough look, the DESCRIBE response did not carry the actual SDP media description. I'm not sure if that's because how you logged the message.
Where did you insert the log lines? Is it where I mentioned in the pull request?
@claincly
I did it in the
RtspMessageChannel.addLine(byte[])
.
More specifically after
String line = new String(lineBytes, /* offset= */ 0, /* length= */ lineBytes.length - 2, CHARSET);
Here i just log the lines.
I can also attach a kind of "sequence of actions":
RtspMessageChannel.addLine(byte[])
. We parsed status code, then content-base, content-type, cseq, content-length.state = STATE_READING_RTSP_BODY;
and return messageLines = null
RtspMessageChannel.handleRtspMessage(byte)
messageLines is still null, because there is also body which is not parsed.RtspMessageChannel.handleRtspMessageLine
, trying to find \r\n.Thanks for the detailed comment. Can you try printing out the received bytes in handleRtspMessageLine()
, after every dataInputStream.readByte()
? My suspicion is your camera (RTSP server) did not insert a CRLF (\r\n
) at the end of the message body, although it should (not necessarily a must).
I am pretty confident that it doesn't have CRLF at the end.
But for additional information, I logged the bytes I am receiving. There are 700 bytes, but I will include last 10 of them.
114 111 108 58 118 105 100 101 111 10
. There is LF, but no CR.
Probably have to use dataInputStream.read()
instead and then also check for -1 as EOF signal besides checking for CRLF
I advanced a little bit, changed DESCRIBE parser so it splits by LF instead of CRLF, but now it crashes on SessionDescription.Builder.build()
, because there is no session name. Now, I am not particularly good with RTSP, so I can't tell if this parameter is really mandatory, but just for the sake of testing, I am sending you the DESCRIBE lines I am receiving.
v=0
o=- 3831122695 3831122695 IN IP4 192.168.1.114
c=IN IP4 192.168.1.114
t=0 0
a=tool:*confidential*
a=recvonly
a=type:broadcast
a=charset:UTF-8
a=control:*
m=video 0 RTP/AVP 96
a=rtpmap:96 H264/90000
a=fmtp:96 packetization-mode=1;sprop-parameter-sets=Z00AKpY1QPAET8s3AQEBAg==,aO48gA==
a=control:video
So there are two things:
According to the byte you posted, the last three chars are 'e'
, 'o'
, and a LF '\n'
(that match the DESCRIBE response you posted in the second comment), indeed the receiver will freeze if it's constantly looking for CRLF. I'll see what we can do on our side.
The SDP (RFC2327) requires a session name (under a s=
tag). I can see that the tool attribute in the DESCRIBE is confidential, I am assuming you have control over the server, I would suggest tweak the server a bit to include a session name.
Another thing that pops to my mind,
You said in one of the comments that you received over 700 bytes, but the Content-Length header you posted is only 303 bytes long. Are they the same media?
Another thing that pops to my mind,
You said in one of the comments that you received over 700 bytes, but the Content-Length header you posted is only 303 bytes long. Are they the same media?
I was wrong, it equals to content length.
So there are two things:
- The SDP (RFC2327) requires a session name (under a
s=
tag). I can see that the tool attribute in the DESCRIBE is confidential, I am assuming you have control over the server, I would suggest tweak the server a bit to include a session name.
I don't have an exact access to the server, since my server is my client's camera, but i notified my client about it. meanwhile, I will probably drop the session name or assign some arbitrary value
@claincly There is one more thing. As you can see in my SDP, there is no profile-level-id
in fmtp
attribute, which is strictly required in exoplayer. Referring to RFC6184:
If no profile-level-id is present, the Baseline profile, without additional constraints at Level 1, MUST be inferred.
But even better solution would be to parse it from sprop-parameter-sets
. If you decode my sprop-parameter-sets
to hexa, you will get 67 4d 00 2a 96 35 40 f0 04 4f cb 37 01 01 01 02
, where 4d 00 2a
is actually profile-level-id
@k1llrogg Thanks for pointing out. Could you post another issue on the profile-level-id
though, it is out of this issue's scope.
As for the CRLF line terminator thing, we are working on a solution.
Use case description
I'm trying to connect to RTSP stream with URI:
rtsp://admin:heslo123@1.2.3.4:554/Streaming/Channels/101?transportmode=unicast&profile=Profile_1
. It doesn't work. I get the following error:2021-05-14 15:39:05.812 21279-21443 E/ExoPlayerImplInternal: Playback error com.google.android.exoplayer2.ExoPlaybackException: Source error at com.google.android.exoplayer2.ExoPlayerImplInternal.handleMessage(ExoPlayerImplInternal.java:580) at android.os.Handler.dispatchMessage(Handler.java:102) at android.os.Looper.loop(Looper.java:223) at android.os.HandlerThread.run(HandlerThread.java:67) Caused by: com.google.android.exoplayer2.source.rtsp.RtspMediaSource$RtspPlaybackException: DESCRIBE 401 at com.google.android.exoplayer2.source.rtsp.RtspMediaSource$SessionInfoListenerImpl.onSessionTimelineRequestFailed(RtspMediaSource.java:219) at com.google.android.exoplayer2.source.rtsp.RtspClient$MessageListener.dispatchRtspError(RtspClient.java:529) at com.google.android.exoplayer2.source.rtsp.RtspClient$MessageListener.onRtspMessageReceived(RtspClient.java:366) at com.google.android.exoplayer2.source.rtsp.RtspMessageChannel$Receiver.lambda$handleRtspMessage$0$RtspMessageChannel$Receiver(RtspMessageChannel.java:291) at com.google.android.exoplayer2.source.rtsp.-$$Lambda$RtspMessageChannel$Receiver$HitD0FATwe-gLFkoSjlAUnzETfA.run(Unknown Source:4) at android.os.Handler.handleCallback(Handler.java:938) at android.os.Handler.dispatchMessage(Handler.java:99) at android.os.Looper.loop(Looper.java:223) at android.os.HandlerThread.run(HandlerThread.java:67) Caused by: com.google.android.exoplayer2.source.rtsp.RtspMediaSource$RtspPlaybackException: DESCRIBE 401 at com.google.android.exoplayer2.source.rtsp.RtspClient$MessageListener.onRtspMessageReceived(RtspClient.java:368) at com.google.android.exoplayer2.source.rtsp.RtspMessageChannel$Receiver.lambda$handleRtspMessage$0$RtspMessageChannel$Receiver(RtspMessageChannel.java:291) at com.google.android.exoplayer2.source.rtsp.-$$Lambda$RtspMessageChannel$Receiver$HitD0FATwe-gLFkoSjlAUnzETfA.run(Unknown Source:4) at android.os.Handler.handleCallback(Handler.java:938) at android.os.Handler.dispatchMessage(Handler.java:99) at android.os.Looper.loop(Looper.java:223) at android.os.HandlerThread.run(HandlerThread.java:67)
Indeed, when I debuigged the requests and responses, the
Authorization: Basic
header is missing. And I couldn't find any way to provide custom headers.Proposed solution
Automaticaly generate the header based on the URI.
Alternatives considered
Provide a way to add custom headers.
Hi , I 'm also facing the same issue , so what is the solution . please help !
Use case description
I'm trying to connect to RTSP stream with URI:
rtsp://admin:heslo123@1.2.3.4:554/Streaming/Channels/101?transportmode=unicast&profile=Profile_1
. It doesn't work. I get the following error:Indeed, when I debuigged the requests and responses, the
Authorization: Basic
header is missing. And I couldn't find any way to provide custom headers.Proposed solution
Automaticaly generate the header based on the URI.
Alternatives considered
Provide a way to add custom headers.