In trying to fulfill a partial range request using chrome as the video playback tool, the video playback start playing back but on reaching halfway, it freezes as if the client is still waiting on more data. At this point the server has already sent the entire request. Please observe the following requests and response and the code used to send the range requested:
Range:bytes=0-
Accept-Ranges:bytes
Content-Length:546827
Content-Range:bytes 0-546827/546828
Content-Type:video/webm
Accept-Ranges:bytes
Content-Length:6155
Content-Range:bytes 540672-546827/546828
Content-Type:video/webm
Accept-Ranges:bytes
Content-Length:1
Content-Range:bytes 546827-546827/546828
Content-Type:video/webm
Is the second response handled correctly? Cause it freezes on this request and starts making multiple requests again until the request times out.
Code handling the request:
private static void copyStream (PullBufferStream inputStream, OutputStream outputStream, BytesStreamObject bytesWrittenObj, int from, int to) throws IOException, CodecNotAvailableException {
//export media stream
BufferedOutputStream bout = new BufferedOutputStream (outputStream);
Buffer b = new Buffer();
long skipped =0;
int byteLength = 0;
do {
inputStream.read(b);
int len = b.getLength();
byte[] data = (byte[]) b.getData();
int offset = b.getOffset();
if (len > 0) {
if(skipped < from) {
// skip bytes until reach from position in inputstream
if(skipped + len <= from) {
// skip all bytes just read
skipped += len;
} else {
// skip only some of bytes read
offset += (from - skipped);
len -= (from - skipped);
skipped = from;
}
}
if(to >= 0) {
if(from + byteLength + len <= to) {
// use the full buffer
} else {
// trim len to needed bytes to be read to prevent exceeding the "to" input parameter
len = (to + 1) - (from + byteLength);
}
}
byteLength+= len;
bytesWrittenObj.bytesWritten = byteLength;
bout.write(data, offset, len);
//bout.write(data, from, end);
}
} while (!b.isEOM());
//fileProperties.setBytesLength(byteLength);
//bout.close();
}
I needed to ensure I flush the outputstream but also made some changes on the actual file size rather than using stream.available().
Related
I followed the Android Guide to build a Bluetooth connection.
To separate things and make them independent, I decided to take the sending part of the BT to a separated thread. To achieve this, I pass the "OutStream" of the BT-Socket to a separated Thread class. My problem is, as soon as I start this thread, the incoming messages are not well red anymore.
But I don't know why, because I do not use this Thread at the moment. It is started but no messages are written in it.
This is part of the "ConnectedToDevice"-Class which receives the messages. I use a special way to detect that my Messages are received completely.
public void run() {
byte[] buffer = new byte[1024];
int bytes;
sendPW();
int len = 0;
Communication.getInstance().setFrequentSending(OVS_CONNECTION_IN_PROGRESS);
Communication.getInstance().setSendingMessages(mmOutStream); //Passing the OutStream to the sending class.
Communication.getInstance().setReceivingMessages(queueReceivingMsg);
Communication.getInstance().startThreads(); //currently: only start sending thread.
while (true) {
try {
bytes = this.mmInStream.read(buffer, len, buffer.length - len);
len += bytes;
if (len >= 3 && buffer[2] != -1) {
len = 0;
Log.d(TAG, "run: To Short?");
} else if (len >= 5) {
int dataLength = Integer
.parseInt(String.format("%02X", buffer[3]) + String.format("%02X", buffer[4]), 16);
if (len == 6 + dataLength) {
queueReceivingMsg.add(buffer);
Log.d(TAG, "run: Added to queue");
len = 0;
}
Log.d("BSS", "dataLenght: " + Integer.toString(dataLength) + " len " + len);
}
} catch (IOException var5) {
cancel();
Communication.getInstance().interruptThreads();
return;
}
}
}
The important part of sending message Class
public static BlockingQueue<Obj_SendingMessage> sendingMessages = new LinkedBlockingQueue<>();
#Override
public void run() {
while (!isInterrupted()) {
if (bGotResponse){
try{
sendingMessage = sendingMessages.take();
send(sendingMessage.getsData());
bGotResponse = false;
lTime = System.currentTimeMillis();
} catch (InterruptedException e){
this.interrupt();
}
}
if((System.currentTimeMillis()%500 == 0) && System.currentTimeMillis() <= lTime+1000){
if(sendingMessage != null){
send(sendingMessage.getsData());
}
} else {
bGotResponse =true;
}
}
}
//Where the outStream is used
private void write(int[] buffer) {
try {
for (int i : buffer) {
this.mmOutputStream.write(buffer[i]);
}
} catch (IOException var3) {
}
}
To be clear again, the sendingMessages is empty all the time, but still the messages get not Received correctly anymore.
Here's a proposal how robust code for reading messages from the stream could look like. The code can handle partial and multiple messages by:
Waiting for more data if a message is not complete
Processing the first message and saving the rest of the data if data for more than one message is available
Searching for a the marker byte 0xff and retaining the data for the next possibly valid message if invalid data needs to be discard
While writing this code I've noticed another bug in the code. If a message is found, the data is not copied. Instead the buffer is returned. However, the buffer and therefore the returned message might be overwritten by the next message before or while the previous one is processed.
This bug is more severe than the poor decoding of the stream data.
private byte[] buffer = new byte[1024];
private int numUnprocessedBytes = 0;
public void run() {
...
while (true) {
try {
int numBytesRead = mmInStream.read(buffer, numUnprocessedBytes, buffer.length - numUnprocessedBytes);
numUnprocessedBytes += numBytesRead;
processBytes();
} catch (IOException e) {
...
}
}
}
private void processBytes() {
boolean tryAgain;
do {
tryAgain = processSingleMessage();
} while (tryAgain);
}
private boolean processSingleMessage() {
if (numUnprocessedBytes < 5)
return false; // insufficient data to decode length
if (buffer[2] != (byte)0xff)
// marker byte missing; discard some data
return discardInvalidData();
int messageLength = (buffer[3] & 0xff) * 256 + (buffer[4] & 0xff);
if (messageLength > buffer.length)
// invalid message length; discard some data
return discardInvalidData();
if (messageLength > numUnprocessedBytes)
return false; // incomplete message; wait for more data
// complete message received; copy it and add it to the queue
byte[] message = Arrays.copyOfRange(buffer, 0, messageLength);
queueReceivingMsg.add(message);
// move remaining data to the front of buffer
if (numUnprocessedBytes > messageLength)
System.arraycopy(buffer, messageLength, buffer, 0, numUnprocessedBytes - messageLength);
numUnprocessedBytes -= messageLength;
return numUnprocessedBytes >= 5;
}
private boolean discardInvalidData() {
// find marker byte after index 2
int index = indexOfByte(buffer, (byte)0xff, 3, numUnprocessedBytes);
if (index >= 3) {
// discard some data and move remaining bytes to the front of buffer
System.arraycopy(buffer, index - 2, buffer, 0, numUnprocessedBytes - (index - 2));
numUnprocessedBytes -= index - 2;
} else {
// discard all data
numUnprocessedBytes = 0;
}
return numUnprocessedBytes >= 5;
}
static private int indexOfByte(byte[] array, byte element, int start, int end) {
for (int i = start; i < end; i++)
if (array[i] == element)
return i;
return -1;
}
I am still trying to get this html 5 playback of video & audio with jetty to work error free. For large videos I notice the video plays up to 75% (just an estimate) then jumps to the end.This never happens with shorter videos but consistently happens on videos more than 10 minutes or so. I don't see any errors in the logs or browser debugger window. Here are the range requests and also the couple errors that do get logged but I don't think are related.
Video
Status Code:206 Partial Content
Accept-Ranges:bytes
Content-Length:16001618
Content-Range:bytes 0-16001617/16001618
Content-Type:video/webm
Server:Jetty(9.3.z-SNAPSHOT)
REQUEST
Connection:keep-alive
Range:bytes=0-
Audio
Status Code:206 Partial Content
Accept-Ranges:bytes
Content-Length:9044858
Content-Range:bytes 0-9044857/9044858
Content-Type:audio/wav
REQUEST
Range:bytes=0-
Video
Status Code:206 Partial Content
Accept-Ranges:bytes
Content-Length:10834
Content-Range:bytes 15990784-16001617/16001618
Content-Type:video/webm
REQUEST
Connection:keep-alive
Range:bytes=15990784-
Video
Status Code:206 Partial Content
Accept-Ranges:bytes
Content-Length:12069458
Content-Range:bytes 3932160-16001617/16001618
Content-Type:video/webm
REQUEST
Connection:keep-alive
Range:bytes=3932160-
Like I said this happens consistently for larger videos so I don't think the videos are corrupted. But no errors are returned except the following which does not stop video playing anyway based on the time this error occurs:
Accept-Ranges: bytes
Content-Range: bytes 0-11229438/11229439
Content-Length: 11229439
Content-Type: video/webm
, Content-Length=11229439, ScreenID=117435}: {1},
com.service.,java.nio.channels.ClosedChannelException
at
org.eclipse.jetty.util.IteratingCallback.close(IteratingCallback.java:427)
at org.eclipse.jetty.server.HttpConnection.onClose(HttpConnection.java:489)
Code that handles the requests:
if(parameters.containsKey(Constants.PARAM_CONTENT_LENGTH)) {
totalLength = java.lang.Math.toIntExact((long)
parameters.get(Constants.PARAM_CONTENT_LENGTH));
} else {
String range =
parameters.get(Constants.PARAM_RANGE_REQUEST).toString();
String[] ranges = range.split("=")[1].split("-");
from = Integer.parseInt(ranges[0]);
if(from >= 0 && from < totalLength) {
to = (totalLength - 1);
if (ranges.length == 2) {
to = Integer.parseInt(ranges[1]);
if (to >= totalLength) {
to = (int) (totalLength - 1);
}
}
} else {
to = -1;
}
}
}
return new ContentRange(from, to, totalLength);
Code that writes to the stream:
BufferedOutputStream bout = new BufferedOutputStream (outputStream);
Buffer b = new Buffer();
long skipped =0;
int byteLength = 0;
do {
inputStream.read(b);
int len = b.getLength();
byte[] data = (byte[]) b.getData();
int offset = b.getOffset();
if (len > 0) {
if(skipped < from) {
if(skipped + len <= from) {
skipped += len;
} else {
offset += (from - skipped);
len -= (from - skipped);
skipped = from;
}
}
if(to >= 0) {
if(from + byteLength + len <= to) {
} else {
len = (to + 1) - (from + byteLength);
}
}
byteLength+= len;
bytesWrittenObj.bytesWritten = byteLength;
bout.write(data, offset, len);
}
} while (!b.isEOM());
bout.flush();
Is there a way to read all InputStream values at once without a need of using some Apache IO lib?
I am reading IR signal and saving it from the InputStream into the byte[] array. While debugging, I have noticed that it works only if I put a delay there, so that I read all bytes at once and then process it.
Is there a smarter way to do it?
CODE:
public void run() {
Log.i(TAG, "BEGIN mConnectedThread");
byte[] buffer = new byte[100];
int numberOfBytes;
removeSharedPrefs("mSharedPrefs");
// Keep listening to the InputStream while connected
while (true) {
try {
// Read from the InputStream
numberOfBytes = mmInStream.read(buffer);
Thread.sleep(700); //If I stop it here for a while, all works fine, because array is fully populated
if (numberOfBytes > 90){
// GET AXIS VALUES FROM THE SHARED PREFS
String[] refValues = loadArray("gestureBuffer", context);
if (refValues!=null && refValues.length>90) {
int incorrectPoints;
if ((incorrectPoints = checkIfGesureIsSameAsPrevious(buffer, refValues, numberOfBytes)) < 5) {
//Correct
} else {
//Incorrect
}
}
saveArray(buffer, numberOfBytes);
}else{
System.out.println("Transmission of the data was corrupted.");
}
buffer = new byte[100];
// Send the obtained bytes to the UI Activity
mHandler.obtainMessage(Constants.MESSAGE_READ, numberOfBytes, -1, buffer)
.sendToTarget();
} catch (IOException e) {
Log.e(TAG, "disconnected", e);
connectionLost();
// Start the service over to restart listening mode
BluetoothChatService.this.start();
break;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Edit:
My old answer is wrong, see EJPs comment! Please don't use it. The behaviour of ByteChannels depend on wether InputStreams are blocking or not.
So this is why I would suggest, you just copy IOUtils.read from Apache Commons:
public static int read(final InputStream input, final byte[] buffer) throws IOException {
int remaining = buffer.length;
while (remaining > 0) {
final int location = buffer.length - remaining;
final int count = input.read(buffer, location, remaining);
if (count == -1) { // EOF
break;
}
remaining -= count;
}
return buffer.length - remaining;
}
Old answer:
You can use ByteChannels and read into a ByteBuffer:
ReadableByteChannel c = Channels.newChannel(inputstream);
ByteBuffer buf = ByteBuffer.allocate(numBytesExpected);
int numBytesActuallyRead = c.read(buf);
This read method is attempting to read as many bytes as there is remaining space in the buffer. If the stream ends before the buffer is fully filled, the number of bytes actually read is returned. See JavaDoc.
I'm writing a code to read the bytes of a request body and this requires knowing the Content-Length or Transfer-encoding ahead of time to safely transfer the message to the client. According to the RCF2616 Section 14.13:
Any Content-Length greater than or equal to zero is a valid value.
In my code Implementation, I achieved this by getting the Content-Length: header field which returns 0,which I guess is a valid response but not the required amount of bytes. Have tried in the below code to read the InputStream from the socket still the amount is achieved but this seem to be failing.Any pointers achieving this? Can provide more code if necessary.
Here is the calling method to get content-length header and read the bytes in chunk till the exact amount is achieved:
//Gets the Content-Length header value
int contLengthOffset = Integer.parseInt(newRequest.getHeaderField("Content-Length"));
int Offset = contLengthOffset;
if(Offset >= 0) {
//Any Content-Length greater than or equal to zero is a valid value.
count = QueryStreamClass.ReadFullyHelper(socket.getInputStream(), Offset);
}
Below is the method that reads the content-length:
/**
* Read the content-length to determine the transfer-length of the message.
* We need enough bytes to get the required message.
* #param Stream
* #param size
*/
public static String ReadFullyHelper(InputStream Stream, int size) {
int Read;
int totalRead = 0;
int toRead = GMInjectHandler.buffer;;
StringBuilder Request = new StringBuilder();
if(toRead > size) {
toRead = size;
}
while(true) {
try {
final byte[] by = new byte[toRead];
Read = Stream.read(by, 0, toRead);
if(Read == -1){
break;
}
Request.append(new String(by, 0, Read));
totalRead += Read;
if (size - totalRead < toRead) {
toRead = size - totalRead;
}
if (totalRead == size) {
break;
}
} catch (IOException e) {
Log.e(TAG, "Error reading stream", e);
}
}
return Request.toString();
}
'This seems to be failing' is not a problem description, but:
public static String ReadFullyHelper(InputStream Stream, int size) {
You can reduce this entire method to the following:
DataInputStream din = new DataInputStream(Stream);
byte[][ buffer = new byte[size];
din.readFully(buffer);
return new String(buffer, 0, size); // or you may want to use a specific encoding here
Ok so i'm trying to send files with the sockets. This is the code i got on the server side. The problem is that if i let this code run i will get some strange output liking missing bytes. I tried to add thread.sleep() inside the if statement this seemed to help some and only made the problem appear when it was handling larger files. I also tried to place a breakpoint on the line buffer = setbuffersize(binaryData, i, in); so i stopped after each file. This seemed to help the problem. So i guess the problem is that the code continue to read even if there is nothing to read (the client has had time to send it yet). I'm not really sure how to fix this. I tried to check if BUFFER_SIZE was as big as in.available() but it seems to stop sending after a while and never reach the BUFFER_SIZE.
So how do i get the code to wait for the data to be transmitted before it tries to read it?
while (byteRead != -1) {
commandlengh = msg.length();
binaryData = new byte[Integer.parseInt(ParameterValues.get(ParameterValues.size() - 1))];
in.read();
byte[] buffer = setbuffersize(binaryData, i, in);
while (in.read(buffer) != -1) {
for (int j = 0; j < buffer.length; j++) {
binaryData[i] = buffer[j];
i++;
}
buffer = setbuffersize(binaryData, i, in);
if(buffer.length == 0)
{
Parameters.clear();
ParameterValues.clear();
i = 0;
commandlengh = 0;
break;
}
}
byteRead = in.read();
}
private byte[] setbuffersize(byte[] binaryData, int i, InputStream in) throws IOException
{
int BUFFER_SIZE = 65536;
if(binaryData.length - i < BUFFER_SIZE)
{
BUFFER_SIZE = binaryData.length - i;
}
else
{
BUFFER_SIZE = 65536;
}
byte[] buffer = new byte[BUFFER_SIZE];
return buffer;
}
You are ignoring the count returned by read() and assuming that it filled the buffer. Check the Javadoc. It isn't required to do that.
I can't make much sense of your code. You don't need all that to read from a socket. Just call read, check the result for -1; if so, stop; otherwise process bytes 0..count-1 of the buffer.