I have been attempting to write a java HTTP server from scratch for my chat program that sends data over websockets and everything has been going well until I started trying to send large images with my program using google chrome. I discovered that when chrome is sending multiple frames of data to my server, it never sends the proper length of the data. The first frame is always fine, but all frames after the initial one send a number that is significantly smaller than the data that the frame contains. For example, it will send a frame saying that it contains 123 bytes of data, when it is really sending over 100 kilobytes. I have tested other browsers like firefox and safari (on my phone) and they don't seem to split up large data into frames like chrome does, so they don't have the same issue. Here are some screenshots of my results:
Initial frame:
enter image description here
Following frames (This is where it fails):
enter image description here
And here is my server side code that gets the data from an input stream and writes it to a file:
byte[] headerdata = new byte[12];
input.read(headerdata, 0, 2);
int frame = (headerdata[0] & 0b10000000) & 0xFF;
long length = (headerdata[1] & 0b01111111) & 0xFF;
System.out.format("Before: %d, Frame: %d%n", length, frame);
if(length == 127){
input.read(headerdata, 0, 12);
length = ((long)(headerdata[0] << 56) & 0xFFFFFFFFFFFFFFFFL | (long)(headerdata[1] << 48) & 0xFFFFFFFFFFFFFFL | (long)(headerdata[2] << 40) & 0xFFFFFFFFFFFFL | (long)(headerdata[3] << 32) & 0xFFFFFFFFFFL | (headerdata[4] << 24) & 0xFFFFFFFFL | (headerdata[5] << 16) & 0xFFFFFFL | (headerdata[6] << 8) & 0xFFFFL | headerdata[7] & 0xFFL)
} else if(length == 126){
input.read(headerdata, 6, 6);
length = ((headerdata[6] << 8) & 0xFFFF | headerdata[7] & 0xFF);
}else{
input.read(headerdata, 8, 4);
}
System.out.println("After: " + length);
int count;
int total = 0;
byte[] buffer = new byte[16384];
boolean done = false;
while(done == false){
count = input.read(buffer, 0, buffer.length);
//System.out.println(count);
for(int i = 0; i < count; i++){
buffer[i] = (byte)(buffer[i] ^ headerdata[(total & 3) + 8]);
total++;
}
fileoutput.write(buffer, 0, count);
System.out.format("Count: %d Left: %d%n", count, (length - total));
if(total >= length){
done = true;
}
}
if(frame == 128) break;
Any explanation as to why I might be failing to read the length of following frames would be greatly appreciated (I have a few suspicions of my own as well). Thank you for your time and efforts.
I found out that the buffer ends up overflowing, resulting in data being misread or lost, which is why I was getting the wrong length values from the frames. I fixed it by adding an if statement that checks to see if the number of bytes read are approaching the length value of that frame, and then only read what is left instead of the full buffer size to stop it from overflowing. Here is the code with the modification:
byte[] headerdata = new byte[12];
input.read(headerdata, 0, 2);
int frame = (headerdata[0] & 0b10000000) & 0xFF;
long length = (headerdata[1] & 0b01111111) & 0xFF;
System.out.format("Before: %d, Frame: %d%n", length, frame);
if(length == 127){
input.read(headerdata, 0, 12);
length = ((long)(headerdata[0] << 56) & 0xFFFFFFFFFFFFFFFFL | (long)(headerdata[1] << 48) & 0xFFFFFFFFFFFFFFL | (long)(headerdata[2] << 40) & 0xFFFFFFFFFFFFL | (long)(headerdata[3] << 32) & 0xFFFFFFFFFFL | (headerdata[4] << 24) & 0xFFFFFFFFL | (headerdata[5] << 16) & 0xFFFFFFL | (headerdata[6] << 8) & 0xFFFFL | headerdata[7] & 0xFFL);
}else if(length == 126){
input.read(headerdata, 6, 6);
length = ((headerdata[6] << 8) & 0xFFFF | headerdata[7] & 0xFF);
}else{
input.read(headerdata, 8, 4);
}
System.out.println("After: " + length);
int count;
int total = 0;
byte[] buffer = new byte[16384];
boolean done = false;
while(done == false){
if(length - total > buffer.length) count = input.read(buffer, 0, buffer.length);
else count = input.read(buffer, 0, (int) (length - total));
//System.out.println(count);
for(int i = 0; i < count; i++){
buffer[i] = (byte)(buffer[i] ^ headerdata[(total & 3) + 8]);
total++;
}
fileoutput.write(buffer, 0, count);
//System.out.format("Count: %d Left: %d%n", count, (length - total));
if(total >= length){
done = true;
}
}
if(frame == 128) break;
I also did some research on different ways to read byte streams from a socket and found that it would also be possible to use DataInputStream.readFully().
Related
I am attempting to convert 16 bit audio into 12 bit audio. However, I am quite inexperienced with such conversions and believe my approach is possibly incorrect or flawed.
The use case, as context for the code snippets below, is an Android app which the user can speak into and that audio is transmitted to an IoT device for immediate playback. The IoT device expects audio in mono 12 bit, 8k sample rate, little endian, unsigned, with the data stored in the first twelve bits (0-11) and final four bits (12-15) are zeroes. Audio data needs to be received in packets of 1000 bytes.
The audio is being created in the Android app through the use of AudioRecord. The instantiation of which is as follows:
int bufferSize = 1000;
this.audioRecord = new AudioRecord(
MediaRecorder.AudioSource.MIC,
8000,
AudioFormat.CHANNEL_IN_MONO,
AudioFormat.ENCODING_PCM_16BIT,
bufferSize
);
In a while loop, the AudioRecord is being read from by 1000 byte packets and modified to the specifications in the use case. Not sure this part is relevant, but for completeness:
byte[] buffer = new byte[1000];
audioRecord.read(buffer, 0, buffer.length);
byte[] modifiedBytes = convert16BitTo12Bit(buffer);
Then the modifiedBytes are sent off to the device.
Here are the methods which modify the bytes. Basically, to conform to the specifications, I am shifting the bits in each 16 bit set (tossing the least significant 4) and adding zeroes to the final four spots. I do this through BitSet.
/**
* Takes a byte array presented as 16 bit audio and converts it to 12 bit audio through bit
* manipulation. Packets must be of 1000 bytes or no manipulation will occur and the input
* will be immediately returned.
*/
private byte[] convert16BitTo12Bit(byte[] input) {
if (input.length == 1000) {
for (int i = 0; i < input.length; i += 2) {
Log.d(TAG, "convert16BitTo12Bit: pass #" + (i / 2));
byte[] chunk = new byte[2];
System.arraycopy(input, i, chunk, 0, 2);
if (!isEmptyByteArray(chunk)) {
byte[] modifiedBytes = convertChunk(chunk);
System.arraycopy(
modifiedBytes,
0,
input,
i,
modifiedBytes.length
);
}
}
return input;
}
Log.d(TAG, "convert16BitTo12Bit: Failed - input is not 1000 in length; it is " + input.length);
return input;
}
/**
* Converts 2 bytes 16 bit audio into 12 bit audio. If the input is not 2 bytes, the input
* will be returned without manipulation.
*/
private byte[] convertChunk(byte[] chunk) {
if (chunk.length == 2) {
BitSet bitSet = BitSet.valueOf(chunk);
Log.d(TAG, "convertChunk: bitSet starts as " + bitSet.toString());
modifyBitSet(bitSet);
Log.d(TAG, "convertChunk: bitSet ends as " + bitSet.toString());
return bitSet.toByteArray();
}
Log.d(TAG, "convertChunk: Failed = chunk is not 2 in length; it is " + chunk.length);
return chunk;
}
/**
* Removes the first four bits and shifts the rest to leave the final four bits as 0.
*/
private void modifyBitSet(BitSet bitSet) {
for (int i = 4; i < bitSet.length(); i++) {
bitSet.set(i - 4, bitSet.get(i));
}
if (bitSet.length() > 8) {
bitSet.clear(12, 16);
} else {
bitSet.clear(4, 8);
}
}
/**
* Returns true if the byte array input contains all zero bits.
*/
private boolean isEmptyByteArray(byte[] input) {
BitSet bitSet = BitSet.valueOf(input);
return bitSet.isEmpty();
}
Unfortunately, this approach produces subpar results. The audio is quite noisy and it is difficult to make out what someone is saying (but you can hear that words are being spoken).
I also have been playing around with just saving the bytes to a file and playing it back on Android through AudioTrack. I noticed that if I just remove the first four bits and do not shift anything, the audio actually sounds good, as such:
private void modifyBitSet(BitSet bitSet) {
bitSet.clear(0, 4);
}
However, when played through the device, it sounds even worse, and I don't even think I can make out any words.
Clearly, my approach is not working here. Central question is how would one convert a 16 bit chunk into 12 bit audio and maintain audio quality given the requirement that the final four bits must be zero? Additionally, given my larger approach of using AudioRecord to obtain the audio, would such a solution for the prior question fit this use case?
Please let me know if there is anything more I can provide to clarify these questions and my intent.
Given that the audio is 16 bits but must be changed to 12 with four zeros at the end, four bits somewhere do have to be tossed.
Yes, of course and there is no other way, is there?
This is something quick that I can comeout with right now. Certainly not fully tested though. Only tested with input of 2 and 4 bytes. I'll leave it to you to test it.
//Reminder :: Convert as many as possible.
//Reminder :: To calculate the required size for store:
//if((bytes.length & 1) == 0) Math.round((bytes.length * 6) / 8F) : Math.round(((bytes.length - 1) * 6) / 8F).
//Return :: Amount of converted bytes.
public static final int convert16BitTo12Bit(final byte[] bytes, final byte[] store)
{
final int size = bytes.length;
int storeIndex = 0;
//Copy the first 2 bytes into store.
store[storeIndex++] = bytes[0]; store[storeIndex] = bytes[1];
if(size < 4) {
store[storeIndex] = (byte)(store[storeIndex] & 0xF0);
return 2;
}
final int result;
final byte tmp;
// 11111111 11110000 00000000 00000000
//+ 11111111 11110000 (<< 12)
//= 11111111 11111111 11111111 00000000 (1)
//-----------------------------------------
// 11111111 00000000 00000000 00000000 (1)
//+ 11111111 11110000 (<< 16)
//= 11111111 11111111 11110000 00000000 (2)
//-----------------------------------------
// 11110000 00000000 00000000 00000000 (2)
//+ 1111 11111111 0000 (<< 20)
//= 11111111 11111111 00000000 00000000 (3)
//-----------------------------------------
// 00000000 00000000 00000000 00000000 (3)
//+ 11111111 11110000 (<< 24)
//= 11111111 11110000 00000000 00000000
for(int i=2, shiftBits = 12; i < size; i += 2) {
if(shiftBits == 24) {
//Copy 2 bytes from bytes[] into store[] and move on.
store[storeIndex] = bytes[i];
//Never store byte 0 (Garbage).
tmp = (byte)(bytes[i + 1] & 0xF0); //Bit order: 11110000.
if(tmp != 0) store[++storeIndex] = tmp;
shiftBits = 12; //Reset
} else if(shiftBits == 20) {
result = ((store[storeIndex - 1] << 24) | ((store[storeIndex] & 0xFF) << 16))
| (((bytes[i] & 0xFF) << 20) | ((bytes[i + 1] & 0xFF) << 12));
store[storeIndex] = (byte)((result >> 24) & 0xFF);
tmp = (byte)((result >> 16) & 0xFF);
//Never store byte 0 (Garbage).
if(tmp != 0) store[++storeIndex] = tmp;
shiftBits = 24;
} else if(shiftBits == 16) {
result = ((store[storeIndex - 1] << 24) | ((store[storeIndex] & 0xFF) << 16))
| (((bytes[i] & 0xFF) << 16) | ((bytes[i + 1] & 0xFF) << 8));
store[storeIndex] = (byte)((result >> 16) & 0xFF);
tmp = (byte)((result >> 8) & 0xF0);
//Never store byte 0 (Garbage).
if(tmp != 0) store[++storeIndex] = tmp;
shiftBits = 20;
} else {
result = ((store[storeIndex - 1] << 24) | ((store[storeIndex] & 0xFF) << 16))
| (((bytes[i] & 0xFF) << 12) | ((bytes[i + 1] & 0xFF) << 4));
store[storeIndex] = (byte)((result >> 16) & 0xFF);
tmp = (byte)((result >> 8) & 0xFF);
//Never store byte 0 (Garbage).
if(tmp != 0) store[++storeIndex] = tmp;
shiftBits = 16;
}
}
return ++storeIndex;
}
Explanations
result = ((store[storeIndex - 1] << 24) | ((store[storeIndex] & 0xFF) << 16))
| (((bytes[i] & 0xFF) << 20) | ((bytes[i + 1] & 0xFF) << 12));
What this does is basically merge two integers into one.
((store[storeIndex - 1] << 24) | ((store[storeIndex] & 0xFF) << 16))
The first one is make an integer with same constant bit position.
(((bytes[i] & 0xFF) << 20) | ((bytes[i + 1] & 0xFF) << 12));
The latter is for 2 current bytes with different bit positions.
(...) | (...)
Pipe or vertical bar at the middle is to merge these two integers we've just created into one.
Usage
To use this method is pretty straight forward.
byte[] buffer = new byte[1000];
byte[] store;
if((buffer.length & 1) == 0) { //Even.
store = new byte[Math.round((bytes.length * 6) / 8F)];
} else { //Odd.
store = new byte[Math.round(((bytes.length - 1) * 6) / 8F)];
}
audioRecord.read(buffer, 0, buffer.length);
int convertedByteSize = convert16BitTo12Bit(buffer, store);
System.out.println("size: " + convertedByteSize);
I have discovered a solution that produces clear audio. First, it is important to recount the requirements for the use case, which is 12 bit unsigned mono audio which will be read in little endian by the device in packets of 1000 bytes.
The initialization and configuration of the AudioRecord as described in the question is fine.
Once the 1000 bytes of audio is read from AudioRecord, it can be put into a ByteBuffer and defined as little endian for modification, and then put into a ShortBuffer to do manipulation on the 16 bit level:
// Audio specifications of device is in little endian.
ByteBuffer byteBuffer = ByteBuffer.wrap(input).order(ByteOrder.LITTLE_ENDIAN);
// Turn into a ShortBuffer so bitwise manipulation can occur on the 16 bit level.
ShortBuffer shortBuffer = byteBuffer.asShortBuffer();
Next, in a loop, take each short and modify it to 12 bit unsigned:
for (int i = 0; i < shortBuffer.capacity(); i++) {
short currentShort = shortBuffer.get(i);
shortBuffer.put(i, convertShortTo12Bit(currentShort));
}
This can be accomplished by shifting the 16 bits four spaces to the right to turn it into 12 bit signed. Then, to convert to unsigned, add 2048. For our purposes as a safety step, we also mask the least significant four bits as required by device, but given the shifting and adding, it shouldn't be the case that any bits actually remain there:
private static short convertShortTo12Bit(short input) {
int inputAsInt = input;
inputAsInt >>>= 4;
inputAsInt += 2048;
input = (short) (inputAsInt & 0B0000111111111111);
return input;
}
If one wishes to return 12 bits to 16 bits, do the reverse for each short (subtract 2048 and shift four spaces to the left).
I want to create an app that can open binary file, read and edit it's content in decimal values, and then save it back to binary file.
This file that I want to open have multiple parameters written in 2 bytes. e.g. 00 06 00 50 01 AB = 6, 80, 427
So far I managed to open bin file, put all data from file into byte[].
Here is the code to open and read file:
String path = getFilesDir().getAbsolutePath();
File file = new File(path + "/file.bin");
int size = (int) file.length();
byte[] bytes = new byte[size];
FileInputStream fis = new FileInputStream(file);
try {
fis.read(bytes);
...
} finally {
fis.close();
}
Here is first attempt to convert byte[] into deciaml values:
for (int i = 0; i < size / 2; i = i + 2) {
intArray[i] = ((bytes[i] & 0xff) << 8) | (bytes[i + 1] & 0xff);
}
But in this code only every second value is correct. It's shows 6,0,80,0,1 and should be 6,80,1.
The second idea was to convert byte[] into String[] and then into e.g. int[] but It doesn't work as intended. In this fragment I didn't parse whole array, but only hexArray[4] which was 50 in hex and 80 in dec, but the result was 50.
for (int i = 0; i < size; i = i + 2) {
num1 = String.format("%02X", bytes[i] & 0xFF);
num2 = String.format("%02X", bytes[i + 1] & 0xFF);
String twoByte = num1 + num2;
hexArrayWithNull[i] = twoByte;
}
String[] hexArray = Arrays.stream(hexArrayWithNull).filter(Objects::nonNull).toArray(String[]::new);
num = String.format("%d", Integer.parseInt(hexArray[4]));
I'm quite stuck here and need some guidance. I must say also that I'm new in programming.
Thanks
You increment i in each iteration by 2 and you store in intArray[i]. So, you store in index [0] then index [2] then index [4] and so on. This explains the zeros at odd indices [1], [3], ...
Also, you must continue the loop until size, not size/2.
Here is the correct loop:
for (int i = 0; i < size; i = i + 2) {
intArray[i/2] = ((bytes[i] & 0xff) << 8) | (bytes[i + 1] & 0xff);
}
Here is the another possibility:
for (int i = 0; i < size/2; i = i + 1) {
intArray[i] = ((bytes[2*i] & 0xff) << 8) | (bytes[2*i + 1] & 0xff);
}
[EDIT]
The reverse operation would be:
for (int i = 0; i < size/2; i = i + 1) {
bytes[2*i] = (intArray[i] & 0xff00) / 256; //or >>8 instead of /256
bytes[2*i+1] = intArray[i] & 0xff;
}
I know there are many similar questions in here, but I have some weird case. What I want to do is to convert a byte[4] to an int.
Here is the conversion from int to byte:
int data24bit = 51;
for (int i = 0; i < 3; i++) {
data8bit = (byte)(data24bit & 0x0000FF);
data24bit = data24bit >> 8;
byte_file.put(data8bit);
}
So far is clear enough. After that I want to read this 4 bytes to get the 51 back. I tried to do it by different ways:
Reading 4 bytes:
byte[] bytes = new byte[4];
for (int i = 0; i < 3; i++) {
byte b = dis.readByte();
bytes[i] = b;
}
// bytes[3] = (byte)(0x000000);
Convert bytes to int:
int value = 0;
value = ((0xFF & bytes[0]) << 24) | ((0xFF & bytes[1]) << 16) |
((0xFF & bytes[2]) << 8) | (0xFF & bytes[3]);
or
value = ByteBuffer.wrap(bytes).getInt();
or
value = new BigInteger(bytes).intValue();
I always get 855638016 as result where 51 is expected.
When I debug the code and look into the byte array I can see the following content: [51, 0, 0, 0].
What am I doing wrong?
The problem is that you're writing the bytes in little-endian (least significant byte first), but read it back assuming big-endian.
After writing it out, your byte array looks like this:
[51, 0, 0, 0]
Then you're trying to convert that back into an integer, like in this example from your post:
value = ((0xFF & bytes[0]) << 24)
| ((0xFF & bytes[1]) << 16)
| ((0xFF & bytes[2]) << 8)
| (0xFF & bytes[3]);
If you fill in the actual values, that calculation is basically this:
value = 51 * 256 * 256 * 256
+ 0 * 256 * 256
+ 0 * 256
+ 0
= 855638016
While what you actually want is this:
value = 0 * 256 * 256 * 256
+ 0 * 256 * 256
+ 0 * 256
+ 51
= 51
The fixed calculation would thus be this:
value = ((0xFF & bytes[3]) << 24)
| ((0xFF & bytes[2]) << 16)
| ((0xFF & bytes[1]) << 8)
| (0xFF & bytes[0]);
Ok stupid enough but I just didn't preserve the byte order.
[51, 0, 0, 0] -> is 855638016
[0, 0, 0, 51] -> is 51
I am trying to understand an implementation of a b+ Tree. I don't understand what this overloaded method exactly do. Why in the first method having Inputsteam is as an argument declare 4 variables which are i1,i2,i3 and i4. In the second method using ObjectInput in as an argument, i understand that it returns a byte from 0 to 255, why is result=251? It will be helpful to explain each line and what it do.
First method:
public final static int readLuposInt(final InputStream is) throws IOException {
final int i1 = is.read();
if (i1 < 0) {
return i1;
}
final int i2 = is.read();
if (i2 < 0) {
return i2;
}
final int i3 = is.read();
if (i3 < 0) {
return i3;
}
final int i4 = is.read();
if (i4 < 0) {
return i4;
}
return (0xFF & i1) | ((0xFF & i2) | ((0xFF & i3) | (0xFF & i4) << 8) << 8) << 8;
}
overloaded method:
public final static int readLuposInt(final ObjectInput in) throws IOException {
final int i0 = in.read();
if (i0 <= 251){
return i0;
}
int result = 251;
int offset = 1;
for (int i = 1; i <= i0 - 251; i++) {
result += in.read() * offset;
offset <<= 8;
}
return result;
}
You could have used a debugger to find the following result.
The first method reads an 4 byte integer from an input stream. It seems to be stored as little-endian value.
the bytes are read in sequentially
ff any of the bytes is missing, -1 is returned.
to return the complete integer, a computation is made by shifting the more significant bytes to the left.
Example:
The number 2293742 represents the hex number 22 FF EE, which will be stored in reverse order: 0xEE 0xFF 0x22 0x00
now the data gets read
i1 = 0xEE
i2 = 0xFF
i3 = 0x22
i4 = 0x00
now the return value is computed:
(0xFF & i4) << 8 = (0xFF & 0x00) << 8 = 0x0000
((0xFF & i3) | (0xFF & i4) << 8) << 8) = ((0x22 | 0x0000) << 8) = (0x0022 << 8) = 0x002200
((0xFF & i2) | ((0xFF & i3) | (0xFF & i4) << 8) << 8) << 8 = (0xFF | 0x002200) << 8 = 0x0022FF00
(0xFF & i1) | ((0xFF & i2) | ((0xFF & i3) | (0xFF & i4) << 8) << 8) << 8 = 0xEE | 0x0022FF00 = 0x0022FFEE
The second method reads unicode characters from the stream, encoded in an UTF-8 encoding. Much can be said about unicode and their character encodings, see Wikipedia how that is working.
I can't understand how to get RGBA values from a short[] that I get from bufferedImage.getRaster().dataBuffer.getData() if dataBuffer is an instance of DataBufferUShort.
How to convert these values (that can be even -30000) to 0..255? If dataBuffer is an instance of DataBufferByte I can simply make something like this:
result[i] = (array[i] < 0) ?
array[i] + 256 :
array[i];
But what should I do with DataBufferUShort? Some PNG images has this type instead of expecting DataBufferByte.
getType() returns TYPE_CUSTOM. Here is the image: http://i.stack.imgur.com/YwmkO.png
A DataBufferUShort can hold multiple types of samples, so you first need to determine what data you have. Here are the most common ones:
If the image data represents 16 bit gray samples, all you need to do scale each value down to get an 8 bit gray value. You can do that by shifting the values 8 bits to the right.
DataBufferUShort dataBuffer;
short[] data = dataBuffer.getData();
byte[] gray = new byte[data.length];
for (int i = 0; i < data.length; i++) {
gray[i] = (byte) ((data[i] & 0xff00) >> 8);
}
If the image data represents 16 bits per sample (A)RGB values, you can do just the same as above, you will just have 3 (or 4 if there's alpha) samples or array elements per pixel instead of one.
If the data represents 16 bits per sample ARGB (as seems to be the case with your sample), you can also convert to int packed ARGB samples, like this:
DataBufferUShort dataBuffer;
short[] data = dataBuffer.getData();
int[] argb = new byte[data.length / 4];
for (int i = 0; i < data.length; += 4) {
int a = (data[i ] & 0xff00) >> 8;
int r = (data[i + 1] & 0xff00) >> 8;
int g = (data[i + 2] & 0xff00) >> 8;
int b = (data[i + 3] & 0xff00) >> 8;
argb[i / 4] = a << 24 | r << 16 | g << 8 | b;
}
If the image data represents 15/16 bit RGB (like the TYPE_USHORT_555_RGB or TYPE_USHORT_565_RGB) you'll have to scale the RGB values up to the full 8 bit/sample range. Something like:
DataBufferUShort dataBuffer;
short[] data = dataBuffer.getData();
byte[] rgb = new byte[data.length * 3];
for (int i = 0; i < data.length; i++) {
int shortRGB = data[i] & 0xffff;
// Assuming 5 bit R, 5 bit G, 5 bit B, using the lower 15 bits
rgb[i * 3 + 0] = ((((shortRGB & 0x7C00) >> 10) + 1) * 8) - 1;
rgb[i * 3 + 1] = ((((shortRGB & 0x03E0) >> 5) + 1) * 8) - 1;
rgb[i * 3 + 2] = ((((shortRGB & 0x001F) ) + 1) * 8) - 1;
}
For 565 RGB or even 4444 ARGB (as used in some Android devices), the procedure is very similar.