I'm creating a fairly simple Android app for work that will plug a URI into the system video player so that users can easily watch the RTSP stream that we are sending from our Wowza server.
In general, I basically just have a play button that calls:
Uri myURI = Uri.parse("rtsp://ipaddress:1935/path/myStream");
Intent intent = new Intent(Intent.ACTION_VIEW, myURI);
startActivity(intent);
but we don't stream from this URI 24/7 and I would like to change the behavior during onCreate so that it will validate the URI before drawing the button, giving me the opportunity to give the user visual feedback that the stream isn't live and save us unneeded support emails.
I've looked at the Uri and URI classes to see if there was a method that I could call to check the URI but nothing seems to really exist. Other URI related questions seem to refer to local files so the dev can just check a file, but I don't think that would work for a live stream. Any advice would be greatly appreciated!
Basically you want to open a plain client socket connection to the RTSP server hostname/port and then post a RTSP OPTIONS request.
If the server responds with a "RTSP/1.0 200 OK", it means the streaming service is up, otherwise it's down.
I wrote a quick and dirty sample RTSP check and tested it out. Feel free to adapt it to your needs:
Update handling UI operations
In your activity class, create these fields:
private static final int MESSAGE_RTSP_OK = 1;
private static final int MESSAGE_RTSP_ERROR = -1;
private Handler handler;
in your onCreate method, add:
handler = new Handler(){
#Override
public void handleMessage(Message msg) {
switch (msg.what){
case MESSAGE_RTSP_OK:
//show_player();
// implement ui operation here
break;
case MESSAGE_RTSP_ERROR:
//show_error();
break;
}
}
};
Then run this sample code:
Updated rtsp check code
new Thread() {
public void run() {
try {
Socket client = new Socket("a1352.l1857053128.c18570.g.lq.akamaistream.net", 554);
OutputStream os = client.getOutputStream();
os.write("OPTIONS * RTSP/1.0\n".getBytes());
os.write("CSeq: 1\n\n".getBytes());
os.flush();
//NOTE: it's very important to end any rtsp request with \n\n (two new lines). The server will acknowledge that the request ends there and it's time to send the response back.
BufferedReader br =
new BufferedReader(
new InputStreamReader(
new BufferedInputStream(client.getInputStream())));
StringBuilder sb = new StringBuilder();
String responseLine = null;
while (null != (responseLine = br.readLine()))
sb.append(responseLine);
String rtspResponse = sb.toString();
if(rtspResponse.startsWith("RTSP/1.0 200 OK")){
// RTSP SERVER IS UP!!
handler.obtainMessage(MESSAGE_RTSP_OK).sendToTarget();
} else {
// SOMETHING'S WRONG
handler.obtainMessage(MESSAGE_RTSP_ERROR).sendToTarget();
}
Log.d("RTSP reply" , rtspResponse);
client.close();
} catch (IOException e) {
// NETWORK ERROR such as Timeout
e.printStackTrace();
handler.obtainMessage(MESSAGE_RTSP_ERROR).sendToTarget();
}
}
}.start();`
For this test I picked the public NASA TV rtsp server: rtsp://a1352.l1857053128.c18570.g.lq.akamaistream.net:554
The code sends the following rtsp request:
OPTIONS * RTSP/1.0
CSeq: 1
And receives the following response:
RTSP/1.0 200 OK
Server: QTSS-Akamai/6.0.3 (Build/526.3; Platform/Linux; Release/Darwin; )
Cseq: 1
Public: DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE, OPTIONS, ANNOUNCE, RECORD
If you are looking for more complex checks, feel free to dive into the rtsp protocol documentation and enhance the code per your own needs: https://www.ietf.org/rfc/rfc2326.txt
Please let me know if more information is needed.
Related
I am attempting to using an IOIO-RTG board to control a MCP-4131 digital potentiometer via SPI. I'm new to SPI but I believe that I've followed the SPI example. I'm able to set a resistance apparently but IOIO remains stuck afterwards. The only way to continue is to disconnect and reconnect to the board. I note that the SPI example expects a MISO and MOSI pin whereas the pot has a combined SDI/SDO pin. Is this difference the source of my issue?
IOIO RTG
IOIOLIb 0326
Application Firmware 0506
Bootloader Firmware 0402
Hardware Sprk 0020
I've tried to implement asynchronous transactions to not wait for a response but the end result is the same. I've called the highgear function from within the Looper class and outside with no change.
class Looper extends BaseIOIOLooper
{
SpiMaster spi;
protected void setup() throws ConnectionLostException
{
int clkPin = 39;//left side = 36
int misoPin = 38;//left side = 33, not expecting output
int mosiPin = 38;//left side = 35
spi = ioio_.openSpiMaster(new DigitalInput.Spec(misoPin,
Mode.PULL_UP), new DigitalOutput.Spec(mosiPin),
new DigitalOutput.Spec(clkPin),
new DigitalOutput.Spec[] { new DigitalOutput.Spec(40), new DigitalOutput.Spec(37), },
new SpiMaster.Config(Rate.RATE_125k, true, true));
}
public void highgear()
{
byte[] request = new byte[] {0,0,0,0,0,5,5,5};
byte[] response = new byte[4];
try {
SpiMaster.Result result = spi.writeReadAsync(0, request, request.length, 7, response, 0);
} catch (ConnectionLostException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
The expected outcome is that the MCP with give the desired resistance and the IOIO will be available for further commanding. There are no errors as the board just freezes in it's set configuration.
The shared SDO/SDI pin of the MCP-4131 should not be the problem.
From the datasheet on page 31: "The 8-lead Single Potentiometer devices are pin limited so the SDO pin is multiplexed with the SDI pin (SDI/SDO pin). After the Address/Command (first 6-bits) are received, If a valid Read command has been requested, the SDO pin starts driving the requested read data onto the SDI/SDO pin."
As long as you only write to the digital potentiometer everything should be the same as with other SPI devices.
Have you tried your code with other SPI devices or even without connecting one?
For some reason HttpURLConnection appears to be buffering the upload data no matter what I try. I can show the progress percentage of the data, but it is clear that the progress advances way too fast while the data is not flowing at that high rate.
The receiving server is not in the intranet, but hosted somewhere. The edge router is throttling the upload bandwidth to 2mbit in order to simulate a slow network, and in the bandwidth graph of the router I can see the data rate graph for the development device. The WiFi AP also allows me to see a graph of the data rate, and it looks just like the one of the edge router, so no device in the intranet is buffering the data. It is definitely the development device (Nexus 5X)
The following is the code that is being used:
HttpURLConnection hucConnection = (HttpURLConnection) url.openConnection();
//hucConnection.setUseCaches(false); // does not solve the issue
//hucConnection.setDefaultUseCaches(false); // does not solve the issue
//hucConnection.setAllowUserInteraction(true); // does not solve the issue
hucConnection.setConnectTimeout(6 * 1000);
hucConnection.setReadTimeout(30 * 1000);
hucConnection.setRequestProperty("content-type", "application/json; charset=UTF-8");
hucConnection.setRequestMethod("POST");
hucConnection.setDoInput(true);
hucConnection.setDoOutput(true);
// Data to transfer
byte[] bData = joTransfer.toString().getBytes("UTF-8");
int iDataLength = bData.length;
//hucConnection.setRequestProperty("content-transfer-encoding", "binary"); // does not solve the issue
// use compression
hucConnection.setRequestProperty("content-encoding", "deflate");
ByteArrayOutputStream stream = new ByteArrayOutputStream();
Deflater deflater = new Deflater(Deflater.DEFAULT_COMPRESSION);
DeflaterOutputStream zip = new DeflaterOutputStream(stream, deflater);
zip.write(bData);
zip.close();
deflater.end();
byte[] bZippedData = stream.toByteArray();
Integer iZippedDataLength = bZippedData.length;
int iChunk = 1000;
hucConnection.setChunkedStreamingMode(iChunk);
//hucConnection.setFixedLengthStreamingMode(iZippedDataLength); // does not solve the issue
hucConnection.connect();
OutputStream osOutputStream = hucConnection.getOutputStream();
// FROM HERE ---->>>
int iUploadedLength;
for (iUploadedLength = 0; iUploadedLength < iZippedDataLength - iChunk; iUploadedLength += iChunk) {
LogWrapper.e(TAG, "l -> f:" + iUploadedLength + " t:" + (iUploadedLength+iChunk));
osOutputStream.write(Arrays.copyOfRange(bZippedData, iUploadedLength , iUploadedLength+iChunk));
osOutputStream.flush();
}
LogWrapper.e(TAG, "r -> f:" + iUploadedLength + " t:" + iZippedDataLength);
osOutputStream.write(Arrays.copyOfRange(bZippedData, iUploadedLength, iZippedDataLength));
osOutputStream.flush();
osOutputStream.close();
// <<<---- TO HERE ---- XXXXXXXXX max 1 second XXXXXXXXX
// FROM HERE ---->>>
int iResponseCode = hucConnection.getResponseCode();
// <<<---- TO HERE ---- XXXXXXXXX about 10 seconds XXXXXXXXX
if (iResponseCode != HttpURLConnection.HTTP_OK) {
...
I expected the calls to osOutputStream.flush(); to force the HttpURLConnection to send the data to the server, but for some reason that isn't happening.
It appears to get buffered somewhere, because after the osOutputStream.close(); and before the hucConnection.getResponseCode(); the data is getting uploaded to the server.
All the transfers (upload and download) are working properly, no data is damaged.
Is there a way to fix this, or an alternative to using HttpURLConnection? I've read that the Socket class does not have this problem, but I'm not sure if it handles redirects and stuff like that properly. I don't need to use cookies or some other stuff.
The aprox. 10 seconds it takes for hucConnection.getResponseCode(); to finish is when about 3MB are uploaded (3MB*8b/B = 24Mb, 24Mb/2Mb/s = 12s), the data that is downloaded is getting sent after that call. The progress of the downloaded data is precise.
Is it possible that a 3rd party library is altering HttpURLConnection's behavior and doing some proxying? Like Firebase or something? I already disabled Crashlytics, but I think that Firebase also does some kind of stats gathering (response time). I think I had some strange issues about 1-2 months ago in another app, where I was getting a Proxy error issue in the domain name resolution, as if something inside of Android was proxying network traffic.
I'm about to give OkHttp a try, one of their recipies has a Post Streaming example (https://github.com/square/okhttp/wiki/Recipes)
Update: I implemented it using okhttp3, following the above mentioned recipie. I have the exact same problem there.
This is on Android 8.1
The server is an nginx instance.
I also ran the app on a Genymotion emulator instance, same OS, and it looks like it's better there, yet the problem still seems to be present, a bit. While radical throttling on the edge router has no effect on the Nexus 5X, it does have an effect on the emulator. But nonetheless, even the emulator upload tracking precision leaves much to be desired.
Would it make sense to use a WebSocket connection for that? That would be my last resort.
The logic is for downloading used in AsyncTask, but I think, that it should be the same (just a switching input>output and so on)
InputStream inputStream = null;
try {
try {
OutputStream outputStream = new FileOutputStream(documentFile, false);
try {
inputStream = httpConn.getInputStream();
byte[] buffer = new byte[4 * 1024]; // or other buffer size
long downloaded = 0;
long target = dataLength;
int readed;
long updateSize = target / 10;
long updateHelp = 0;
while ((readed = inputStream.read(buffer)) != -1) {
downloaded += readed;
updateHelp += readed;
if (updateHelp >= updateSize) {
updateHelp = 0;
publishProgress(downloaded, target);
}
outputStream.write(buffer, 0, readed);
if (isCancelled()) {
return false;
}
}
outputStream.flush();
outputStream.close();
return true;
} catch (Exception e) {
return false;
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (inputStream != null) {
inputStream.close();
}
}
} catch (Exception e) {
e.printStackTrace();
}
I am setting a HttpCookie in my android application associated with a domain within the cookieManager. Then, when I stream HLS videos using exo player, I want this cookie to used for each of the .ts chunk requests.
This is the code, I use to store the cookie in the Application section of the project:
String url1 = "http://sdtest.vzvisp.com";
URI auri1 = null;
try {
auri1 = new URI(url1);
} catch (URISyntaxException e) {
e.printStackTrace();
}
AppGlobal.cookieManager = new CookieManager(null, CookiePolicy.ACCEPT_ALL);
AppGlobal.cookieManager.getCookieStore().add(auri1, new HttpCookie("JSESSIONID","aaaMp0uKke4gp"));
AppGlobal.cookieManager.getCookieStore().add(auri1,new HttpCookie("JSESSIONID1","aaaMp0uKke4gasasasp"));
AppGlobal.currentHandler = CookieHandler.getDefault();
if (AppGlobal.currentHandler != AppGlobal.cookieManager) {
CookieHandler.setDefault(AppGlobal.cookieManager);
}
This is an example of a chunk request sent :
http://sdtest.vzvisp.com:22779/AppConfig/SIT/fios/hls/fios/fios_hls_1m_00004.ts?vzSvc=fU2h73FMzhPgj8w0VNqYYsQ3lVZq8jjIWr6Xfrmraq4=&vispVzKey=54038752&vispIconFg=1&vispUsr=ZPq5BrbWoSQm1nsCNTPfBA==&vispAuthSid=CIAAASAIuwE&vispExpTime=1481081538&vispAuthKey=36958733&vispAuthSign=7.23.GMoCIpxmT_k123kT3P8iJxmCF-BWmeiAXQontU11hUI
But, when I inspect the packet, I do not see the cookie sent. Can someone kindly help me out ?
Thanks.
Try doing something like this:
AppGlobal.cookieManager.setCookie("http://sdtest.vzvisp.com","JSESSIONID=aaaMp0uKke4gp; Domain=.vzvisp.com; Path=/; Version=1");
Check this ticket to see how to set hls cookie correctly:
Hope this help
I have an android app which will create a ServerSocket and accept a socket.
I want it can communicate(read/write) with remote device.
My sample code like following:
mListenSocket = new ServerSocket();
mListenSocket.setReuseAddress(true);
mListenSocket.bind(new InetSocketAddress(DOCK_PORT));
mSocket = mListenSocket.accept();
Thread {
loop {
outputStream = mSyncSocket.getOutputStream();
inputStream = mSyncSocket.getInputStream();
...
inputStream.read(data)
outputStream.write(data);
...
}
}
It can read right inputStream data from client, but the second time read() always return -1 after I write data in outputStream firt time .
I have no idea about this issue.
Somebody can give me some tips? Thanks a lot.
======================================================================
I think I need to express my problem more clearly.
There are two devices(A, B), and their workflow as follows:
Type 1 task:
1. A sends command to B
2. B receives command and reply message to A
3. A receives message, Done.
Type 2 task:
1. A sends command to B, Done.
Above tasks are asynchronized.
My old method was that create a ServerSocket on A and B device respectively to handle A->B and B->A communication.
I think maybe one socket can resolve above task, but one socket will encounter read -1 issue.
Someone can give me more advices? Thanks a lot.
read() returns -1 because the peer has closed the connection. You should do likewise, and stop reading, and stop handling the connection completely. It's finished.
I use something like this:
final DataOutputStream dos = new DataOutputStream(s.getOutputStream());
final BufferedReader in = new BufferedReader(new InputStreamReader(s.getInputStream(), "UTF-8"));
// Send the login data.
final JSONObject login = new JSONObject();
// Send the login message.
dos.write((login.toString() + "\r\n").getBytes());
// Wait until we receive something from the server.
String receivedMessage;
while ((receivedMessage = in.readLine()) != null) {
Log.i(TAG, "Received data: " + receivedMessage);
processMessagesFromServer(dos, receivedMessage);
}
I use a normal socket instead of ServerSocket.
Please check this thread for more information.
I'm communication with a server through a tcp socket connection, i'm able to read lines that ends with \n fine, however when the line is not terminated (ends in \n) i'm not able to read it. I tried the following but it didn't work and caused my app to freeze at startup:
private Socket socket;
private BufferedReader input;
public boolean isConnected;
#Override
public void onCreate()
{
try
{
socket = new Socket ("server.ip.add.ress", 23456);
input = new BufferedReader (new InputStreamReader (socket.getInputStream());
handshake();
isConnected = true;
}
catch // Handle IOException and UnknownHostException
}
// custom runnable to read availabe input from the server
private class MyRunnable implements Runnable
{
private volativle String value;
public String getValue()
{
return value;
}
#Override
public void run()
{
int count;
char[] buffer = new char[10]; // expected message 'username: '
try
{
count = input.read (buffer, 0, 10);
if (count > 0) value = new String (buffer);
}
catch // IOException
}
}
// when connection is established with server expect 'username: ' from
// the server and send the user name back to it
public void handshake()
{
MyRunnable runnable = new MyRunnable();
try
{
Thread thread = new Thread (runnable);
thread.start();
thread.join();
String greeting = runnable.getValue();
if (greeting.equals ("username: ")) // Send username back
}
catch // InterruptedException
}
why is it hanging? and how can i read a non terminated line?
Edit:
To clarify: The server sends the greeting message username: immediately after the connection is established with a client, the client wait for the greeting and send back it's username when received (that's what handshake() does), if no handshake the client disconnects otherwise it start listening for incoming messages. Because i need to know if handshake is complete before starting the listener i had to use Thread.join().
The problem: Thanks for the comments and answers below, it turned out that BufferedReader.read() blocks the thread and waits until something is sent from the server and if nothing is being sent it causes the app to hang, Therefor there's no way to find out if the line has ended.
The solution: In my specific situation i just wanted to know if a specific message is sent "username: " so i used read (buffer, 0, 10) to read exactly 10 characters (the length of "username: "), and because it blocks if nothing is sent i used Thread.join (1000) which waits only one second and then if nothing received i disconnect the client.
Why is it hanging?
This is what it is suppose to be. It will block the thread if no data is available to read. This is also why you want to put it in a background thread.
Can it not just return if nothing is available?
What you are looking for is ready(), which will tell you whether there is available data or not.
Indicates whether this reader is ready to be read without blocking.
Returns
true if this reader will not block when read is called, false if unknown or blocking will occur.
But you should be very careful when using this function. Because networking is a lot about timing. The fact that you don't have any data to read at this second doesn't necessary mean that it won't be any data in the next second.
So a better design of the server should be more or less as the following:
If the username is found, return the username
If the username is not found, return an error message to let the client side know that the username is not found
There's no need for the thread. Your goal is to wait until you've read what you've been waiting for. Why not just let read() perform the wait for you?
What you're struggling with is the classic problem of TCP communication: "when do I know that I've got everything the server sent?"
In your case, you're expecting to read bytes until the collection of bytes ends with "username: ". So, change your algorithm to perform 1 byte reads (filling a buffer as you go) until that buffer ends with "username: ".
You can make a more complicated algorithm -- which would be more efficient -- that would attempt to read multiple bytes at a time and append them to a buffer -- performing your check each time. But either strategy is logically equivalent.
I also recommend just using the InputStreamReader. It has various read() methods. I am a bit suspicious about the BufferedInputReader, especially when dealing with data that isn't newline terminated. I'm probably just paranoid. I've just never used it when writing TCP client/server programs, so I'm not sure.