I'm running into an issue where I'm opening a local file on my Android device and I'm trying to send it to another device that's listening on a port. It's sending the information (I see data in mappedByteBuffer). However, when the data is received on the listener and I view byteBuffer, the data is all blank. Can someone point out what I'm doing wrong? Thanks!
Sender:
WritableByteChannel channel;
FileChannel fic;
long fsize;
ByteBuffer byteBuffer;
MappedByteBuffer mappedByteBuffer;
connection = new Socket(Resource.LAN_IP_ADDRESS, Resource.LAN_SOCKET_PORT);
out = connection.getOutputStream();
File f = new File(filename);
in = new FileInputStream(f);
fic = in.getChannel();
fsize = fic.size();
channel = Channels.newChannel(out);
//other code
//Send file
long currPos = 0;
while (currPos < fsize)
{
if (fsize - currPos < Resource.MEMORY_ALLOC_SIZE)
{
mappedByteBuffer = fic.map(FileChannel.MapMode.READ_ONLY, currPos, fsize - currPos);
channel.write(mappedByteBuffer);
currPos = fsize;
}
else
{
mappedByteBuffer = fic.map(FileChannel.MapMode.READ_ONLY, currPos, Resource.MEMORY_ALLOC_SIZE);
channel.write(mappedByteBuffer);
currPos += Resource.MEMORY_ALLOC_SIZE;
}
}
closeAllConnections(); //closes connection, fic, channel, in, out
Listener
FileChannel foc;
ByteBuffer byteBuffer;
ReadableByteChannel channel;
serverSoc = new ServerSocket(myPort);
connection = serverSoc.accept();
connection.setSoTimeout(3600000);
connection.setReceiveBufferSize(Resource.MEMORY_ALLOC_SIZE);
in = connection.getInputStream();
out = new FileOutputStream(new File(currentFileName));
foc = out.getChannel();
channel = Channels.newChannel(in);
//other code
while (fileSize > 0)
{
if (fileSize < Resource.MEMORY_ALLOC_SIZE)
{
byteBuffer = ByteBuffer.allocate((int)fileSize);
channel.read(byteBuffer);
//byteBuffer is blank!
foc.write(byteBuffer);
fileSize = 0;
}
else
{
byteBuffer = ByteBuffer.allocate(Resource.MEMORY_ALLOC_SIZE);
channel.read(byteBuffer);
//byteBuffer is blank!
foc.write(byteBuffer);
fileSize -= Resource.MEMORY_ALLOC_SIZE;
}
}
closeAllConnections(); //closes connection, foc, channel, in, out, serverSoc
Note:
MEMORY_ALLOC_SIZE = 32768
Found the best way to write to the channels this way is the following (don't use my original way, it produces missing characters and extra spaces):
while (channel.read(byteBuffer) != -1)
{
byteBuffer.flip();
foc.write(byteBuffer);
byteBuffer.compact();
}
byteBuffer.flip();
while (byteBuffer.hasRemaining())
{
foc.write(byteBuffer);
}
Related
Hi guys i'm creating a method to print an invoice from my app, but when i send the bytes to print it doesn't print all of the bytes i'm sending to the printer, the last bytes are getting cut all time resulting in an incomplete invoice, this is the code that i'm using at the moment:
public void Print(string nombreImpresora, string formatoFactura)
{
var listOfDevices = BluetoothAdapter.DefaultAdapter;
if (listOfDevices == null)
throw new Exception("No Bluetooth adapter found.");
if (!listOfDevices.IsEnabled)
throw new Exception("Bluetooth adapter is not enabled.");
var device = (from bd in listOfDevices.BondedDevices
where bd.Name == nombreImpresora
select bd).FirstOrDefault();
if (device == null)
throw new Exception("Named device not found.");
BluetoothSocket socket;
var uuid = device.GetUuids()?.ElementAt(0);
if (uuid != null)
{
socket = device.CreateInsecureRfcommSocketToServiceRecord(uuid.Uuid);
}
else
{
socket = device.CreateInsecureRfcommSocketToServiceRecord(UUID.FromString("00001101-0000-1000-8000-00805f9b34fb"));
}
if (socket.IsConnected)
{
return;
}
socket.Connect();
byte[] completeBuffer = Encoding.ASCII.GetBytes(formatoFactura);
Toast.MakeText(Forms.Context, Convert.ToString(completeBuffer.Length), ToastLength.Short).Show();
var bufferSize = 256;
int completedBufferLength = completeBuffer.Length;
List<byte[]> bufferList = new List<byte[]>();
for (int i = 0; i < completedBufferLength; i = i + bufferSize)
{
byte[] val = new byte[bufferSize];
if (completedBufferLength < i + bufferSize)
{
bufferSize = completedBufferLength - i;
}
Array.Copy(completeBuffer, i, val, 0, bufferSize);
bufferList.Add(val);
}
for (int j = 0; j < bufferList.Count; j++)
{
socket.OutputStream.Write(bufferList[j], 0, bufferList[j].Length);
}
socket.Close();
socket.Dispose();
}
i'm sending a string and converting it to bytes in the method above, the string is a custom invoice that i made with the invoice data from another page of my app.
The printer that i'm using is Bixolon SPP-R310, at this point i don't know if it is a printer related issue really.
¿Can anyone help me with this pls?
thanks in advance
First of all try to append some empty lines at the end of your string using "\n". If the output is still incomplete change device.CreateInsecureRfcommSocketToServiceRecord to device.createRfcommSocketToServiceRecord.
I am working with android.media.midi and I am sending a bunch of midi data to a MidiInputPort with a delay value like this:
long start = System.nanoTime();
if (messages != null)
{
for (int i = 0; i < messages.length(); i++)
{
MidiNote note = MidiHelper.parseMessageForNote(messages.getString(i));
if (note != null)
{
byte[] buffer = new byte[32];
int numBytes = 0;
int channel = 1; // MIDI channels 1-16 are encoded as 0-15.
buffer[numBytes++] = (byte) (note.action + (channel - 1));
buffer[numBytes++] = (byte) note.note;
buffer[numBytes++] = (byte) note.velocity;
long delay = note.delay * 1000000;
midiInputPort.send(buffer, 0, numBytes, start + delay);
start = start + delay;
}
}
midiInputPort.flush();
}
You'll notice that I call flush immediately after sending all the data (just trying to get flush() to work) but it has no effect. The data still gets sent to the Midi port as if I never called flush. The documentation is pretty clear and simple for this function. It says "If you want to cancel events that you have scheduled in the future then call flush()." Is there something about this that I am missing? Any help is appreciated.
Is the output stream still open? Maybe the MIDI device you are sending the flush packet to doesn't support it?
Looking at the google source code, flush() calls MidiPortImpl.packFlush() which sends a single byte array of byte[1]=0x02 to the output stream as long as it is still open.
public void onFlush() throws IOException {
synchronized (mBuffer) {
if (mOutputStream == null) {
throw new IOException("MidiInputPort is closed");
}
int length = MidiPortImpl.packFlush(mBuffer);
mOutputStream.write(mBuffer, 0, length);
}
}
https://android.googlesource.com/platform/frameworks/base/+/master/media/java/android/media/midi/MidiInputPort.java
https://android.googlesource.com/platform/frameworks/base/+/master/media/java/android/media/midi/MidiPortImpl.java
So, I used this Android sample as a guide to make a bluetooth connection without any kind of validation (This app will have a very restrict userbase and will not be available to download at the store).
I was able to transfer string just fine, and it works like a charm. My problem is when trying to transfer images.
I have one activity that sends the byte[] of the image to the bluetooth service and a handler on the other activity that recieves the message and do wharever with the said message.
The thing is, because of the size of the buffer the handler receives parts of the original byte[]. What I'm tryng to do is to merge all the parts in one byte and save it.
This is the loop that I do inside my handler:
byte[] result = new byte[originalByteSize];
byte[] readBuf = (byte[]) msg.obj;
if (cont < byteTimes){
if (result == null) {
result = appendData(readBuf,readBuf);
} else {
result = appendData(result,readBuf);
}
} else {
new SavePhotoTask(cont).execute(result);
}
This is the appendData function
protected byte[] appendData(byte[] firstObject,byte[] secondObject){
ByteArrayOutputStream outputStream = new ByteArrayOutputStream( );
try {
if (firstObject!=null && firstObject.length!=0)
outputStream.write(firstObject);
if (secondObject!=null && secondObject.length!=0)
outputStream.write(secondObject);
} catch (IOException e) {
e.printStackTrace();
}
return outputStream.toByteArray();
}
And here is where I write the file:
public class SavePhotoTask extends AsyncTask<byte[], String, String> {
int counter = 0;
public SavePhotoTask(int cont){
this.counter = cont;
}
#Override
protected String doInBackground(byte[]... jpeg) {
File photo = new File(Environment.getExternalStorageDirectory(), counter + "_photo.jpg");
if (photo.exists()) {
photo.delete();
}
try {
FileOutputStream fos = new FileOutputStream(photo.getPath());
fos.write(jpeg[0]);
fos.close();
} catch (java.io.IOException e) {
Log.e("PictureDemo", "Exception in photoCallback", e);
}
return (null);
}
What I needed is just a tip in the right direction, thanks.
I solved my problem with this answer
The problem was in the way I was writing and reading the stream.
public static void writeItem(OutputStream out, String s) throws IOException
{
// Get the array of bytes for the string item:
byte[] bs = s.getBytes(); // as bytes
// Encapsulate by sending first the total length on 4 bytes :
// - bits 7..0 of length
out.write(bs.length); // modulo 256 done by write method
// - bits 15..8 of length
out.write(bs.length>>>8); // modulo 256 done by write method
// - bits 23..16 of length
out.write(bs.length>>>16); // modulo 256 done by write method
// - bits 31..24 of length
out.write(bs.length>>>24); // modulo 256 done by write method
// Write the array content now:
out.write(bs); // Send the bytes
out.flush();
}
public static String readItem(InputStream in) throws IOException
{
// first, read the total length on 4 bytes
// - if first byte is missing, end of stream reached
int len = in.read(); // 1 byte
if (len<0) throw new IOException("end of stream");
// - the other 3 bytes of length are mandatory
for(int i=1;i<4;i++) // need 3 more bytes:
{
int n = in.read();
if (n<0) throw new IOException("partial data");
len |= n << (i<<3); // shift by 8,16,24
}
// Create the array to receive len bytes:
byte[] bs = new byte[len];
// Read the len bytes into the created array
int ofs = 0;
while (len>0) // while there is some byte to read
{
int n = in.read(bs, ofs, len); // number of bytes actually read
if (n<0) throw new IOException("partial data");
ofs += n; // update offset
len -= n; // update remaining number of bytes to read
}
// Transform bytes into String item:
return new String(bs);
}
I have an Android intentservice for uploading files to server. The files will be added to the intentservice queue for every 1 min.It is working fine in the android devices with OS less than 5.0.1. But in android 5.0.1 while uploading the file, if network is lost, I am not getting any response, not throwing any exception and the intent service is blocked. Files are not uploading from that point. I have to restart the app in order to make the service work again. Below is the code I have in onHandleIntent method.
int ReadBytes = 0;
int BUFSIZ = 4096;
byte Buf[] = new byte[BUFSIZ];
outputStream = conn.getOutputStream();
dataOutputStream = new DataOutputStream(outputStream);
fileInputStream = new FileInputStream(source);
long prevBytesRead = 0;
ReadBytes = fileInputStream.read(Buf, 0, BUFSIZ);
while(ReadBytes>0){
dataOutputStream.write(Buf, 0, ReadBytes);
prevBytesRead += ReadBytes;
long totalbytes = fileInputStream.getChannel().size();
ReadBytes = fileInputStream.read(Buf, 0, BUFSIZ);
}
outputStream.flush();
dataOutputStream.flush();
Any help would be appreciated.
You are probably leaking the stream, you should try wrapping it in a try/catch block:
try {
int ReadBytes = 0;
int BUFSIZ = 4096;
byte Buf[] = new byte[BUFSIZ];
outputStream = conn.getOutputStream();
dataOutputStream = new DataOutputStream(outputStream);
fileInputStream = new FileInputStream(source);
long prevBytesRead = 0;
ReadBytes = fileInputStream.read(Buf, 0, BUFSIZ);
while(ReadBytes>0){
dataOutputStream.write(Buf, 0, ReadBytes);
prevBytesRead += ReadBytes;
long totalbytes = fileInputStream.getChannel().size();
ReadBytes = fileInputStream.read(Buf, 0, BUFSIZ);
}
outputStream.flush();
dataOutputStream.flush();
} catch (IOException e) {
} finally {
// Close streams with an additional try/catch
}
Perhaps you should take a look at Facebook's infer tool: fbinfer.com , very useful for these situations.
I'm trying to download the 3gp video file from Youtube's rtsp URL and save it to my external storage, but it appears that the method I have only works with HTTP URL.
I received this warning:
09-02 10:32:40.877: WARN/System.err(7988): java.net.MalformedURLException: Unknown protocol: rtsp
My download method is the following:
public static void downloadFromURL(String url, File cacheDir, String fileName) throws IOException {
long startTime = System.currentTimeMillis();
String TAG = "DL";
if (url != null) {
if (fileName.contains("/"))
fileName = fileName.replace("/", "-");
url = url.replace(" ", "%20");
URL mUrl = new URL(url);
URLConnection ucon = mUrl.openConnection();
ucon.setReadTimeout(TIMEOUT_CONNECTION);
ucon.setConnectTimeout(TIMEOUT_SOCKET);
File f = new File(cacheDir, fileName);
InputStream is = ucon.getInputStream();
BufferedInputStream inStream = new BufferedInputStream(is, BUFFER_SIZE);
FileOutputStream outStream = new FileOutputStream(f);
byte[] buff = new byte[BUFFER_SIZE];
int len;
while ((len = inStream.read(buff)) != -1) {
outStream.write(buff, 0, len);
}
// clean up
outStream.flush();
outStream.close();
inStream.close();
android.util.Log.d(TAG, "Download completed in " + ((System.currentTimeMillis() - startTime) / 1000) + " sec");
}
}
FYI I test it on my Nexus 7 with Wi-Fi connection.
Let me know if anybody has the solution to the problem I'm having.
UPDATE
Anyway, I found these Objective-C snippets which do the request I need, but I'm clueless to turn it into Java codes:
NSString *urlString = #"http://www.youtube.com/get_video_info?&video_id=7ubt8AWa7SU"; //7ubt8AWa7SU gylfmQgtMJc
NSURL *infoUrl = [NSURL URLWithString:urlString];
NSError *error = NULL;
NSString *info = [NSString stringWithContentsOfURL:infoUrl encoding:NSUTF8StringEncoding error:&error];
if (info == nil)
{
NSLog(#"Error: %#", [error description]);
return;
}
NSArray *urlComponents = [info componentsSeparatedByString:#"&"];
NSArray *itemComponents;
NSString *urlEncodedFmtStreamMap = NULL;
for (NSString *item in urlComponents)
{
itemComponents = [item componentsSeparatedByString:#"="];
if (itemComponents)
{
NSString *first = [itemComponents objectAtIndex:0];
if ([first isEqualToString: #"url_encoded_fmt_stream_map"])
{
urlEncodedFmtStreamMap = [itemComponents objectAtIndex:1];
break;
}
}
}
NSString *type = NULL;
NSString *urlSeg1 = NULL;
NSString *sig = NULL;
if (urlEncodedFmtStreamMap)
{
NSArray *formats = [[urlEncodedFmtStreamMap stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding] componentsSeparatedByString:#","];
for (NSString *item in formats)
{
type = NULL;
urlSeg1 = NULL;
sig = NULL;
NSArray *pairs = [item componentsSeparatedByString:#"&"];
for (NSString *pair in pairs)
{
NSArray *varComps = [pair componentsSeparatedByString:#"="];
NSString *varName = [varComps objectAtIndex: 0];
NSString *varValue = [[varComps objectAtIndex: 1] stringByReplacingPercentEscapesUsingEncoding: NSUTF8StringEncoding];
if ([varName isEqualToString: #"type"])
{
NSArray *typeSegments = [varValue componentsSeparatedByString: #";"];
type = [typeSegments objectAtIndex:0];
}
else if ([varName isEqualToString: #"url"])
{
urlSeg1 = [varValue stringByReplacingPercentEscapesUsingEncoding: NSUTF8StringEncoding];
}
else if ([varName isEqualToString: #"sig"])
{
sig = varValue;
}
}
if ([type isEqualToString:#"video/mp4"] && urlSeg1 && sig)
{
self.videoUrl = [[urlSeg1 stringByAppendingString: #"&signature="] stringByAppendingString: sig];
break;
}
}
}
I don't think there's an easy way of doing it. Getting an RTSP stream is not as simple as a HTTP GET request. The video and audio data packets are sent as RTP, essentially UDP packets with extra data, and then you have RTSP and RTCP (which you can ignore) for server/client communication.
I would recommend looking into the MediaPlayer and Stagefright source code, especially where they set up different data sources and see if you can extract parts of the RTSP handshaking and the setup of the RTP session.