Strange MediaPlayer behavior after reconnection - android

I use own local server as data source for Android MediaPlayer object. When the player connects to the server in first time he sent GET request like this:
GET/ HTTP/1.1
Host: localhost:53087
User-Agent: stagefright/1.1 (Linux;Android 3.2.1)
After for reconnection I recreate the player, but on connection he sends some another request:
GET HTTP/1.1
Host: localhost:53087
Accept: */*
and drops the connection when takes a data immediately. After he sent a request as in the first time and starts playing.
Whats is the reason for so strange behavior?

Related

Okhttp websocket Expected HTTP 101 response but was '400 ' with spring boot server

I am developing an Android application with a Spring Boot server.
So, I open a WebSocket connection with OkHttp from my Android application to the Spring Boot server that uses Stomp.
When the WebSocket connection is closed for some exception thrown in the server and I try to reconnect, I get Expected HTTP 101 response but was '400 ' error.
For testing purposes, I throw an exception when the Android application sends SEND frame to Server.
So this test goes like this
Send WebSocket Upgrade request
Once WebSocket connection is open, send CONNECT Stomp request
Then, send SUBSCRIBE Stomp request to subscribe to a queue
Then, send a message in SEND Stomp Frame, then the server throws an exception
The server will respond ERROR Stomp frame and closes the WebSocket connection.
I try to reconnect by going through step 1-3
Here is the code for testing.
When I first launch my Android application, it sends WebSocket request to the server as follows. I made up the IP address but in my real application, I use the correct one.
val webSocketRequest = Request.Builder().url("199.166.1.100:8081/websocket").build()
socket = okHttpClient.newWebSocket(webSocketRequest, this)
The WebSocket request looks like this
Request{method=GET, url=http://199.166.1.100:8081/websocket, tags={}}
Then, the server sends following response and onOpen() function is called, which means handshake worked fine.
Response{protocol=http/1.1, code=101, message=, url=http://199.166.1.100:8081/websocket}
In onOpen() function, I send a Stomp CONNECT request to the server and the server responds CONNECTED and then I send SUBSCRIBE request to a queue. So far, everything works fine.
I have added an Interceptor to StompMessageBorker to throw an exception for SEND request as follows
public Message<?> preSend(Message<?> message, #NonNull MessageChannel channel) {
StompHeaderAccessor stompHeaderAccessor = StompHeaderAccessor.wrap(message);
StompCommand stompCommand = stompHeaderAccessor.getCommand();
if (StompCommand.SEND.equals(stompCommand))
throw new RuntimeException();
return message;
}
So, I send a message in SEND Stomp frame and when the RuntimeException is thrown, the server sends an ERROR Stomp Frame or response to the Android application, and the onClosing() and onClosed() functions of OkHttp Websocket are called, which means that the WebSocket connection is closed.
Here is the problem, after the WebSocket connection is closed, I try to reconnect the WebSocket with following function and basically goes through the same process, send CONNECT request, then SUBSCRIBE request.
override suspend fun reconnect() {
socket?.close(1000, null)
val webSocketRequest = Request.Builder().url("199.166.1.100:8081/websocket").build()
socket = okHttpClient.newWebSocket(webSocketRequest, this)
}
but I get Expected HTTP 101 response but was '400 ' and even onOpen() function is not called, I guess there is some issue with a handshake.
A couple of things I noticed by carrying out this test.
The first three or four reconnection requests worked as expected, the onOpen() function was called, I could subscribe to the queue as well. But after 3 or 4 times, I start getting this error Expected HTTP 101 response but was '400 ', since then, every reconnection request got the same error.
Once I get this Expected HTTP 101 response but was '400 ', Rebooting my Spring Boot server does not help, but If re-launch my Android application, then I can connect the Websocket. But I end up getting the same error after 3 or 4 disconnections triggered by sending a message which throws an exception.
Do you guys have any idea of this issue? or Am I doing the wrong thing?
How could I establish the WebSocket connection even after 3 or 4 times of disconnection?
Thanks in advance.
=============== UPDATE =================================
I have added a HandshakeInterceptor to see if there is an issue in beforeHandshake().
Obviously, when I get the 101 reponse, the beforeHandshake() is called.
Response{protocol=http/1.1, code=101, message=, url=http://199.166.1.100:8081/websocket}
However, when I get 400 response
Expected HTTP 101 response but was '400 '
The beforeHandshake() is not called at all. How come beforeHandshake() is not even called, because I send a handshake request to the server from Android?
=============== UPDATE =================================
Let me just get things easier.
I did following tests
Run the Spring Boot server
Launch Android application.
WebSocket connection is successfully made.
Reboot the Spring Boot server
WebSocket connection is closed
Android sends HTTP handshake to reconnect the WebSocket and I got Expected HTTP 101 response but was '400 '
I am not sure why rebooting makes the server behave differently.

UDP source port number is missing in the SETUP response message

I am working on a Wifi Display Sink application in android and am facing an issue where the source is not sending the UDP server-port number in the RTSP SETUP message.
The SETUP RESPONSE is as below
'RTSP/1.0 200 OK
cseq: 2
date: Tue, 11 Aug 2015 15:12:38 +0000
server: Mine/1.0
session: 1719935144;timeout=60
transport: RTP/AVP/UDP;unicast;client_port=15550-15551;
'
NOTE:
I have figured out the server-port number using tcpdump in the source device. It is 16660. It doesnot look like any specifically assigned port number as well.Seems like a random port number hardcoded into the source device for Wifi Display Application.
Is there any other way to know the server-port number to which I should listen to for incoming UDP packets?
You should be listening on port 15550 and 15551 and the incoming UDP packets will contain their source port.

android media player - how to disable range request? (broken audio streaming on Nexus 7)

I have a audio streaming app, which runs a local proxy server. The local proxy server makes a http connection to a internet streaming source, gets and buffers locally the streaming data.
Then, inside in the app, I use MediaPlayer to connect to the local proxy server, using the method
mediaPlayer.setDataSource(...); // the url of the local proxy server
Everything was fine (with plenty of Android devices and different OS versions - 1.5...4.0), until Nexus 7 release.
In Nexus 7, the media player refuses to play the source from the local proxy server.
When I took a look at the logs, seems like the MediaPlayer uses range requests internally.
My local proxy server doesn't handle that. It returns HTTP/1.0 200 OK and the data.
However, the media player doesn't like that and throws an exception:
Caused by: libcore.io.ErrnoException
?:??: W/?(?): [ 07-18 00:08:35.333 4962: 5149 E/radiobee ]
?:??: W/?(?): : sendto failed: ECONNRESET (Connection reset by peer)
?:??: W/?(?): at libcore.io.Posix.sendtoBytes(Native Method)
?:??: W/?(?): at libcore.io.Posix.sendto(Posix.java:146)
?:??: W/?(?): at libcore.io.BlockGuardOs.sendto(BlockGuardOs.java:
?:??: W/?(?): at libcore.io.IoBridge.sendto(IoBridge.java:473)
We requested a content range, but server didn't support that. (responded with 200)
According to the http specs, if the server responds with HTTP/1.0 instead 1.1, the client must not fire a range request (1.0 doesn't support that anyway),
also if the server doesn't support the range request, it should be fine, if it responds with 200 OK (and this is what I'm doing), but the MediaPlayer implementation on Nexus 7 doesn't like that.
I took a look at this thread :
HTTP: How should I respond to "Range: bytes=" when Range is unsupported?
,where they claim that the response with 200 OK must be good enough, but unfortunately it doesn't help.
I'm not sure if this is a problem with Jelly Bean, or a problem with Nexus 7 implementation specifically, but it's still a problem for me which I have to resolve.
Again, there are NO range requests on plenty other Android devices, using the same app. For some reason these range requests are happening now on Nexus 7. (It may happen on other Android devices as well, but again, never happened to me so far).
Any possible way to disable the range requests for MediaPlayer?
If there are none, can anybody suggest a quick fix for my proxy server logic (what exactly it has to return, if it receive this range request?), without changing my other logic, if possible?
Seems like maybe I have to return something like "HTTP/1.0 206 OK\r\nPartial Content\r\n\r\n", but probably there should be some value at the end of the Partial Content - not sure what is should be this one.
Your help would be appreciated.
Thanks..
We've finally solved this in a clean way. In our case we have full control over the streaming server, but i guess you could do this with a local proxy as well. Since Build.VERSION_CODES.ICE_CREAM_SANDWICH it's possible to set headers for the MediaPlayer object. As our app enables the user to seek inside the audio stream, we've implemented this Range header on the client. If the server responds with the proper headers, the MediaPlayer will not try multimple times to request the stream.
That's what our server headers look like now for the android client:
Content-Type: audio/mpeg
Accept-Ranges: bytes
Content-Length: XXXX
Content-Range: bytes XXX-XXX/XXX
Status: 206
The important part is the 206 status code (partial content). If you don't send this header, the android client will try to re-request the source, no matter what.
If your player doesn't allow seeking in the stream, you could simply always set the Range header to 0-some arbitrary large number.
I work on a large scale audio streaming app (that uses a local host HTTP proxy to stream audio to MediaPlayer) and I ran into this issue as soon as I got a JellyBean device in my hands at Google I/O 2012.
When quality testing our application on different devices (and with information received from our automated crash logs and user submitted logs), we noticed that certain MediaPlayer implementations behaved in what I would call an erratic (and sometimes downright psychotic) manner.
Without going into too much detail, this is what I saw: some implementations would make multiple requests (some times 5+) for the same URL. These requests were all slightly different from each other in that each one was for a different byte range (usually for the first or last 128 bytes). The conclusion was that the MediaPlayer was trying to find embedded metadata, then would after some point it would give up and just make a regular non-range request.
That is not what the stock JellyBean MediaPlayer implementation is doing, it just an example of the whacky-ness and general fragmentation of the media framework on Android. However, the solution to the above situation was also the solution to the JellyBean problem, and that is:
Have your local proxy respond with chunked encoding
This replaces the Content-Length header with a Tranfer-Encoding: chunked header. This means the requesting client will not know the total length of the resource and thus cannot make a range-request, it just has to deal with the chunks as they are received.
Like I said this is a hack, but it works. It is not without its side effects: media player buffer progress will be incorrect since it doesn't know the length of the audio (is one of them), to get around that you will have to use your own buffering computation (you are streaming from somewhere through your proxy to MediaPlayer, right? So you will know the total length).
When you seek or skip or the connection is lost and MediaPlayer keeps reconnecting to the proxy server, you must send this response with Status 206 after you get the request and range(int) from the client.
String headers += "HTTP/1.0 206 OK\r\n";
headers += "Content-Type: audio/mpeg\r\n";
headers += "Accept-Ranges: bytes\r\n";
headers += "Content-Length: " + (fileSize-range) + "\r\n";
headers += "Content-Range: bytes "+range + "-" + fileSize + "/*\r\n";
headers += "\r\n";

streaming from Android to VLC

I know similar questions have been asked but I have not been able to find the answer to my SPECIFIC problem.
I am attempting to create a RTSP/RTP video stream from the Android camera to a VLC Player client. I have written a small RTSP server to handle all the the setup and VLC seems to like my parameters. However, after the PLAY command is issued and My app starts sending the video stream (via DatagramPackets) the VLC player does not receive any data.
I am using the jlibrtp library and setup my stream like this
sendSoc = new DatagramSocket(1238);
recSoc = new DatagramSocket(1239);
sess = new RTPSession(sendSoc, recSoc);
FakeClass fc = new FakeClass(); //This implements the RTPAppIntf but all the functions are empty
sess.RTPSessionRegister(fc, null, null);
sess.payloadType(96);
Participant p = new Participant("localhost",1236,1237);
sess.addParticipant(p);
This is the logs I see from VLC player
Opening connection to 192.168.1.221, port 1234...
[0xb1003790] main art finder debug: no art finder module matching "any" could be loaded
[0xb1003790] main art finder debug: TIMER module_need() : 6.331 ms - Total 6.331 ms / 1 intvls (Avg 6.331 ms)
[0x9f653e0] main playlist debug: art not found for rtsp://192.168.1.221:1234
...remote connection opened
Sending request: OPTIONS rtsp://192.168.1.221:1234 RTSP/1.0
CSeq: 2
User-Agent: LibVLC/2.0.1 (LIVE555 Streaming Media v2011.12.23)
Received 76 new bytes of response data.
Received a complete OPTIONS response:
RTSP/1.0 200 OK
CSeq: 2
Public: DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE
Sending request: DESCRIBE rtsp://192.168.1.221:1234 RTSP/1.0
CSeq: 3
User-Agent: LibVLC/2.0.1 (LIVE555 Streaming Media v2011.12.23)
Accept: application/sdp
Received 240 new bytes of response data.
Received a complete DESCRIBE response:
RTSP/1.0 200 OK
CSeq: 3
Content-Type: application/sdp
v=0
o=- 1343306778867 1343306778867 IN IP4 192.168.1.221
s=Droid Stream
i=Live Stream from Android Camera
t=1343306778873 0
m=video 1236/2 RTP/AVP 96
a=rtpmap:96 H264/9000
[0xb0101190] live555 demux debug: RTP subsession 'video/H264'
Sending request: SETUP rtsp://192.168.1.221:1234/ RTSP/1.0
CSeq: 4
User-Agent: LibVLC/2.0.1 (LIVE555 Streaming Media v2011.12.23)
Transport: RTP/AVP;unicast;client_port=1236-1237
Received 128 new bytes of response data.
Received a complete SETUP response:
RTSP/1.0 200 OK
CSeq: 4
Session: 1343306779273
Transport: RTP/AVP/UDP;unicast;client_port=1236-1237;server_port=1238-1239
[0xb5203c18] main input debug: selecting program id=0
[0xb0101190] live555 demux debug: setup start: 0.000000 stop:0.000000
Sending request: PLAY rtsp://192.168.1.221:1234 RTSP/1.0
CSeq: 5
User-Agent: LibVLC/2.0.1 (LIVE555 Streaming Media v2011.12.23)
Session: 1343306779273
Range: npt=0.000-
Received 71 new bytes of response data.
Received a complete PLAY response:
RTSP/1.0 200 OK
CSeq: 5
Session: 1343306779273
Range: npt=0.000-
.
.
[Snip]
.
.
[0xb5203c18] main input debug: `rtsp://192.168.1.221:1234' successfully opened
[0xb0101190] live555 demux warning: no data received in 10s. Switching to TCP
Sending request: TEARDOWN rtsp://192.168.1.221:1234 RTSP/1.0
CSeq: 6
User-Agent: LibVLC/2.0.1 (LIVE555 Streaming Media v2011.12.23)
Session: 1343306779273
So I don't know what is wrong. VLC should be listening on the android device port 1236 but its not seeing the packets so i dont know where it is listening. Can tell me if this looks right?
Found out the issue was writing my packets to the Android device port 1236 instead of the client device port 1236. So the transport parameter in the SETUP command that states
Transport: RTP/AVP/UDP;unicast;client_port=1236-1237;server_port=1238-1239
Is stating the server (Android phone) will send RTP packets from server port 1238 to client device port 1236. Likewise, RTCP communication will occur between server port 1239 and client device port 1237.
Didn't you try to check session? Does it contain packets on port 1236? Is it possible that your FakeClass must contain any functions which must send data to VLC?

HTTP setReadTimeOut can work if Network Not Available after posting data to server and waiting for responce from server

I was Posting Data from Android Mobile to Server over HTTP connection. I was able to POST data to server and also get acknowledgement from Server. But sometime if my server communication thread waiting for acknowledgement from server after posting data that time i loss Internet Connectivity and thread unable to receive Ack from server and goes in infinite loop. I tried setReadTimeOut(180000) method.
Can it work for me? Can it gives SocketTimeOutException if no network Available/ No connection between server and mobile
Setting a timeout will most likely work. However, I would not recommend setting it to 180000 (= 180 seconds = 3 minutes!). 30 seconds is usually more than enough. You also might want to add a setConnectTimeout
Note that there are some situations where an HttpUrlConnection doesn't always work properly:
Android versions 2.2 or earlier, better use the Apache HTTP client for these versions (see also this post)
On the emulator (see also this post)

Categories

Resources