I am trying to convert a bitmap to yuv, and recording this yuv in the ffmpeg frame recorder...
I am getting the video output with only green pixels, though when i check the properties of this video it shows the set Frame rate and the resolution...
The yuv encoding part is correct, but i feel i am making mistake somewhere else, mostly in returning the yuv bytes to recording part ( getByte(byte [] yuv ) because only there the yuv.length displayed in console is 0,, rest all methods return a big value in console ...
Kindly help...
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
directory.mkdirs();
addListenerOnButton();
play=(Button)findViewById(R.id.buttonplay);
stop=(Button)findViewById(R.id.buttonstop);
record=(Button)findViewById(R.id.buttonstart);
stop.setEnabled(false);
play.setEnabled(false);
record.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
startRecording();
getByte(new byte[]{});
}
});
stop.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
stopRecording();
}
});
play.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) throws IllegalArgumentException, SecurityException, IllegalStateException {
Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(String.valueOf(asmileys)));
intent.setDataAndType(Uri.parse(String.valueOf(asmileys)), "video/mp4");
startActivity(intent);
Toast.makeText(getApplicationContext(), "Playing Video", Toast.LENGTH_LONG).show();
}
});
}
......//......
public void getByte(byte[] yuv) {
getNV21(640, 480, bitmap);
System.out.println(yuv.length + " ");
if (audioRecord == null || audioRecord.getRecordingState() != AudioRecord.RECORDSTATE_RECORDING) {
startTime = System.currentTimeMillis();
return;
}
if (RECORD_LENGTH > 0) {
int i = imagesIndex++ % images.length;
yuvimage = images[i];
timestamps[i] = 1000 * (System.currentTimeMillis() - startTime);
}
/* get video data */
if (yuvimage != null && recording) {
((ByteBuffer) yuvimage.image[0].position(0)).put(yuv);
if (RECORD_LENGTH <= 0) {
try {
long t = 1000 * (System.currentTimeMillis() - startTime);
if (t > recorder.getTimestamp()) {
recorder.setTimestamp(t);
}
recorder.record(yuvimage);
} catch (FFmpegFrameRecorder.Exception e) {
e.printStackTrace();
}
}
}
}
public byte [] getNV21(int inputWidth, int inputHeight, Bitmap bitmap) {
int[] argb = new int[inputWidth * inputHeight];
bitmap.getPixels(argb, 0, inputWidth, 0, 0, inputWidth, inputHeight);
byte[] yuv = new byte[inputWidth * inputHeight * 3 / 2];
encodeYUV420SP(yuv, argb, inputWidth, inputHeight);
bitmap.recycle();
System.out.println(yuv.length + " ");
return yuv;
}
void encodeYUV420SP(byte[] yuv420sp, int[] argb, int width, int height) {
final int frameSize = width * height;
int yIndex = 0;
int uIndex = frameSize;
int vIndex = frameSize;
System.out.println(yuv420sp.length + " " + frameSize);
int a, R, G, B, Y, U, V;
int index = 0;
for (int j = 0; j < height; j++) {
for (int i = 0; i < width; i++) {
a = (argb[index] & 0xff000000) >> 24; // a is not used obviously
R = (argb[index] & 0xff0000) >> 16;
G = (argb[index] & 0xff00) >> 8;
B = (argb[index] & 0xff) >> 0;
// well known RGB to YUV algorithm
Y = ((66 * R + 129 * G + 25 * B + 128) >> 8) + 16;
U = ((-38 * R - 74 * G + 112 * B + 128) >> 8) + 128;
V = ((112 * R - 94 * G - 18 * B + 128) >> 8) + 128;
// NV21 has a plane of Y and interleaved planes of VU each sampled by a factor of 2
// meaning for every 4 Y pixels there are 1 V and 1 U. Note the sampling is every other
// pixel AND every other scanline.
yuv420sp[yIndex++] = (byte) ((Y < 0) ? 0 : ((Y > 255) ? 255 : Y));
if (j % 2 == 0 && index % 2 == 0) {
yuv420sp[uIndex++] = (byte) ((U < 0) ? 0 : ((U > 255) ? 255 : U));
yuv420sp[vIndex++] = (byte) ((V < 0) ? 0 : ((V > 255) ? 255 : V));
}
index++;
}
}
}
.....//.....
public void addListenerOnButton() {
image = (ImageView) findViewById(R.id.imageView);
image.setDrawingCacheEnabled(true);
image.buildDrawingCache();
bitmap = image.getDrawingCache();
System.out.println(bitmap.getByteCount() + " " );
button = (Button) findViewById(R.id.btn1);
button.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View view){
image.setImageResource(R.drawable.image1);
}
});
......//......
EDIT 1:
I made few changes in the above code:
record.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
startRecording();
getByte();
}
});
.....//....
public void getbyte() {
byte[] yuv = getNV21(640, 480, bitmap);
So now in the console; i get same yuv length in this method as the yuv length from getNV21 method..
But now i am getting half screen Black and Half screen green(black above and green below) pixels in the recorded video...
If i add these lines to onCreate method;
image = (ImageView) findViewById(R.id.imageView);
image.setDrawingCacheEnabled(true);
image.buildDrawingCache();
bitmap = image.getDrawingCache();
I do get distorted frames( frames are 1/4th of the image displayed with mix up of colors here and there) in the video....
All i am trying to learn is the image processing and flow of Bytes[] from one method to another; but i am still a noob..;
Kindly help..!
Related
I'm working on a video conference app using openvidu. We are trying to include wikitude AR session in the call.
The problem is that both of them requires access to the camera so I have the next scenario: if I instantiate the local participant video first I can't start the wikitude AR session because video don't load. If I instantiate the wikitude session firstly the other participants of the call don't see the device video.
I was able to create a custom video capturer for openvidu, that imitates the camera. It is required to send every frame for it to works.
package org.webrtc;
import android.content.Context;
import android.graphics.Bitmap;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.atomic.AtomicReference;
public class CustomVideoCapturer implements VideoCapturer {
private final static String TAG = "FileVideoCapturer";
//private final FileVideoCapturer.VideoReader videoReader;
private final Timer timer = new Timer();
private CapturerObserver capturerObserver;
private AtomicReference<Bitmap> image = new AtomicReference<Bitmap>();
private final TimerTask tickTask = new TimerTask() {
#Override
public void run() {
tick();
}
};
public CustomVideoCapturer() {
}
public void tick() {
Bitmap frame = image.get();
if (frame != null && !frame.isRecycled()) {
NV21Buffer nv21Buffer = new NV21Buffer(getNV21(frame),frame.getWidth(),frame.getHeight(), null);
VideoFrame videoFrame = new VideoFrame(nv21Buffer, 0, System.nanoTime());
capturerObserver.onFrameCaptured(videoFrame);
}
}
byte [] getNV21(Bitmap image) {
int [] argb = new int[image.getWidth() * image.getHeight()];
image.getPixels(argb, 0, image.getWidth(), 0, 0, image.getWidth(), image.getHeight());
byte [] yuv = new byte[image.getWidth()*image.getHeight()*3/2];
encodeYUV420SP(yuv, argb, image.getWidth(), image.getHeight());
image.recycle();
return yuv;
}
void encodeYUV420SP(byte[] yuv420sp, int[] argb, int width, int height) {
final int frameSize = width * height;
int yIndex = 0;
int uvIndex = frameSize;
int a, R, G, B, Y, U, V;
int index = 0;
for (int j = 0; j < height; j++) {
for (int i = 0; i < width; i++) {
a = (argb[index] & 0xff000000) >> 24; // a is not used obviously
R = (argb[index] & 0xff0000) >> 16;
G = (argb[index] & 0xff00) >> 8;
B = (argb[index] & 0xff) >> 0;
// well known RGB to YUV algorithm
Y = ( ( 66 * R + 129 * G + 25 * B + 128) >> 8) + 16;
U = ( ( -38 * R - 74 * G + 112 * B + 128) >> 8) + 128;
V = ( ( 112 * R - 94 * G - 18 * B + 128) >> 8) + 128;
// NV21 has a plane of Y and interleaved planes of VU each sampled by a factor of 2
// meaning for every 4 Y pixels there are 1 V and 1 U. Note the sampling is every other
// pixel AND every other scanline.
yuv420sp[yIndex++] = (byte) ((Y < 0) ? 0 : ((Y > 255) ? 255 : Y));
if (j % 2 == 0 && index % 2 == 0) {
yuv420sp[uvIndex++] = (byte)((V<0) ? 0 : ((V > 255) ? 255 : V));
yuv420sp[uvIndex++] = (byte)((U<0) ? 0 : ((U > 255) ? 255 : U));
}
index ++;
}
}
}
public void sendFrame(Bitmap bitmap) {
image.set(bitmap);
}
#Override
public void initialize(SurfaceTextureHelper surfaceTextureHelper, Context applicationContext,
CapturerObserver capturerObserver) {
this.capturerObserver = capturerObserver;
}
#Override
public void startCapture(int width, int height, int framerate) {
//timer.schedule(tickTask, 0, 1000 / framerate);
threadCV().start();
}
Thread threadCV() {
return new Thread() {
#Override
public void run() {
while (true) {
if (image.get() != null) {
tick();
}
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
}
#Override
public void stopCapture() throws InterruptedException {
timer.cancel();
}
#Override
public void changeCaptureFormat(int width, int height, int framerate) {
// Empty on purpose
}
#Override
public void dispose() {
//videoReader.close();
}
#Override
public boolean isScreencast() {
return false;
}
private interface VideoReader {
VideoFrame getNextFrame();
void close();
}
/**
* Read video data from file for the .y4m container.
*/
}
On the local participant I than use this function to send the frame:
public void sendFrame(Bitmap frame) {
customVideoCapturer.sendFrame(frame);
}
But I wasn't be able to thake the frames from the wikitude Camera. There is a way to access the frames and resend them?
Such as of the Native Api sdk, version 9.10.0, according to answer from wikitude support
https://support.wikitude.com/support/discussions/topics/5000096719?page=1 , to access the camera frames a custom plugin should be created:
https://www.wikitude.com/external/doc/documentation/latest/androidnative/pluginsapi.html#plugins-api
I'm attempting to use MediaCodec and MediaMuxer to change a series of JPEGs into a mp4.
No matter what I do, I always get a green staticy screen as output on the MP4.
Code Follows:
public class AvcEncoder
{
public bool CanEncode = true;
MediaCodec codec;
MediaMuxer muxer;
MediaFormat format;
public AvcEncoder()
{
codec = MediaCodec.CreateEncoderByType("video/avc");
format = MediaFormat.CreateVideoFormat("video/avc", 720, 480);
format.SetInteger(MediaFormat.KeyBitRate, 700000);
format.SetInteger(MediaFormat.KeyFrameRate, 10);
format.SetInteger(MediaFormat.KeyColorFormat, (int)Android.Media.MediaCodecCapabilities.Formatyuv420planar);
format.SetInteger(MediaFormat.KeyIFrameInterval, 5);
codec.Configure(format, null, null, MediaCodecConfigFlags.Encode);
codec.Start();
Java.IO.File f = new Java.IO.File(Android.OS.Environment.ExternalStorageDirectory, "Parkingdom");
if (!f.Exists())
{
f.Mkdirs();
}
muxer = new MediaMuxer(f.ToString() + "/test.mp4", MuxerOutputType.Mpeg4);
}
public void EncodeFrame(Bitmap image)
{
int mWidth = image.Width;
int mHeight = image.Height;
int[] mIntArray = new int[mWidth * mHeight];
// Copy pixel data from the Bitmap into the 'intArray' array
image.GetPixels(mIntArray, 0, mWidth, 0, 0, mWidth, mHeight);
byte[] byteArray = new byte[mWidth * mHeight * 3 / 2];
// Call to encoding function : convert intArray to Yuv Binary data
EncodeYUV420P(byteArray, mIntArray, mWidth, mHeight);
using (var stream = new MemoryStream())
{
image.Compress(Bitmap.CompressFormat.Png, 100, stream);
byteArray = stream.ToArray();
}
int inputBufferIndex = codec.DequeueInputBuffer(-1);
if (inputBufferIndex >= 0)
{
ByteBuffer buffer = codec.GetInputBuffer(inputBufferIndex);
buffer.Clear();
buffer.Put(byteArray);
codec.QueueInputBuffer(inputBufferIndex, 0, byteArray.Length, 0, 0);
}
}
public void SaveMp4()
{
CanEncode = false;
bool running = true;
MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
int track = -1;
while (running)
{
int index = codec.DequeueOutputBuffer(bufferInfo, 10000);
if (index == (int)MediaCodecInfoState.OutputFormatChanged)
{
MediaFormat format = codec.OutputFormat;
track = muxer.AddTrack(format);
muxer.Start();
}
else if (index == (int)MediaCodecInfoState.TryAgainLater)
{
break;
}
else if (index >= 0)
{
if ((bufferInfo.Flags & MediaCodecBufferFlags.CodecConfig) != 0)
{
bufferInfo.Size = 0;
}
if (track != -1)
{
ByteBuffer outBuffer = codec.GetOutputBuffer(index);
outBuffer.Position(bufferInfo.Offset);
outBuffer.Limit(bufferInfo.Offset + bufferInfo.Size);
muxer.WriteSampleData(track, outBuffer, bufferInfo);
codec.ReleaseOutputBuffer(index, false);
}
}
}
codec.Stop();
codec.Release();
muxer.Stop();
muxer.Release();
CanEncode = true;
}
void EncodeYUV420P(byte[] yuv420p, int[] argb, int width, int height)
{
int frameSize = width * height;
int chromasize = frameSize / 4;
int yIndex = 0;
int uIndex = frameSize;
int vIndex = frameSize + chromasize;
int a, R, G, B, Y, U, V;
int index = 0;
for (int j = 0; j < height; j++)
{
for (int i = 0; i < width; i++)
{
a = (int)(argb[index] & 0xff000000) >> 24; // a is not used obviously
R = (argb[index] & 0xff0000) >> 16;
G = (argb[index] & 0xff00) >> 8;
B = (argb[index] & 0xff) >> 0;
Y = ((66 * R + 129 * G + 25 * B + 128) >> 8) + 16;
U = ((-38 * R - 74 * G + 112 * B + 128) >> 8) + 128;
V = ((112 * R - 94 * G - 18 * B + 128) >> 8) + 128;
yuv420p[yIndex++] = (byte)((Y < 0) ? 0 : ((Y > 255) ? 255 : Y));
if (j % 2 == 0 && index % 2 == 0)
{
yuv420p[uIndex++] = (byte)((U < 0) ? 0 : ((U > 255) ? 255 : U));
yuv420p[vIndex++] = (byte)((V < 0) ? 0 : ((V > 255) ? 255 : V));
}
index++;
}
}
}
}
Each time a new jpeg is generated "EncodeFrame" is called which is supposed to be changing it into a YUV420Planar format for the media codec. The codec I'm testing with doesn't support semiplanar.
In case someone comes across this later I changed
EncodeFrame to use a Surface instead and just used DrawBitmap().
It's slower than the byte copy but is working for my purposes.
trying to record video from camera by using javaCV,
// recoder settings:
private int imageWidth = 320;
private int imageHeight = 240;
private int frameRate = 30;
recorder = new FFmpegFrameRecorder(ffmpeg_link, imageWidth, imageHeight, 1);
recorder.setFormat("mp4");
recorder.setFrameRate(frameRate);
// frame settings:
IplImage yuvIplimage = null;
yuvIplimage = IplImage.create(320, 320, IPL_DEPTH_16U, 1); //32 not supported
//image reader:
private ImageReader mImageReader;
mImageReader = ImageReader.newInstance(320, 320, ImageFormat.YUV_420_888, 10);
mImageReader.setOnImageAvailableListener(
mOnImageAvailableListener, mBackgroundHandler);
private final ImageReader.OnImageAvailableListener mOnImageAvailableListener
= new ImageReader.OnImageAvailableListener() {
#Override
public void onImageAvailable(ImageReader reader) {
Image image = reader.acquireNextImage();// acquireLatestImage(); - also tried
if (image == null)
return;
final ByteBuffer buffer = image.getPlanes()[0].getBuffer();
byte[] bytes = new byte[buffer.remaining()];
buffer.get(bytes, 0, bytes.length);
if (yuvIplimage != null ) {
// OPTION 1
yuvIplimage.getByteBuffer().put(convertYUV420ToNV21(image));
// OPTION 2
//yuvIplimage.getByteBuffer().put(decodeYUV420SP(bytes,320,320));
try {
if (started) {
recorder.record(yuvIplimage);
}
} catch (Exception e) {
e.printStackTrace();
}
}
image.close();
}
};
Option 1 is to decode image to NV21 by using code:
private byte[] convertYUV420ToNV21(Image imgYUV420) {
byte[] rez;
ByteBuffer buffer0 = imgYUV420.getPlanes()[0].getBuffer();
ByteBuffer buffer2 = imgYUV420.getPlanes()[2].getBuffer();
int buffer0_size = buffer0.remaining();
int buffer2_size = buffer2.remaining();
rez = new byte[buffer0_size + buffer2_size];
buffer0.get(rez, 0, buffer0_size);
buffer2.get(rez, buffer0_size, buffer2_size);
return rez;
}
option 2 is to convert to rgb as if i understand corect:
public byte[] decodeYUV420SP( byte[] yuv420sp, int width, int height) {
final int frameSize = width * height;
byte rgb[]=new byte[width*height];
for (int j = 0, yp = 0; j < height; j++) {
int uvp = frameSize + (j >> 1) * width, u = 0, v = 0;
for (int i = 0; i < width; i++, yp++) {
int y = (0xff & ((int) yuv420sp[yp])) - 16;
if (y < 0) y = 0;
if ((i & 1) == 0) {
v = (0xff & yuv420sp[uvp++]) - 128;
u = (0xff & yuv420sp[uvp++]) - 128;
}
int y1192 = 1192 * y;
int r = (y1192 + 1634 * v);
int g = (y1192 - 833 * v - 400 * u);
int b = (y1192 + 2066 * u);
if (r < 0) r = 0; else if (r > 262143) r = 262143;
if (g < 0) g = 0; else if (g > 262143) g = 262143;
if (b < 0) b = 0; else if (b > 262143) b = 262143;
rgb[yp] = (byte) (0xff000000 | ((r << 6) & 0xff0000)
| ((g >> 2) & 0xff00) | ((b >> 10) & 0xff));
}
}
return rgb; }
it looks incorect also.
wisch is the correct way to conert camera2 image to IplImage?
and is it possible to do it on fly?
If the recorder requires NV21, then converting the image to that instead of RGB is likely the fastest option.
But why don't you just use android.media.MediaRecorder? It's much more efficient and can use the hardware encoders.
But if you need to stick with ffmpeg, your first option is incorrect for many devices. In addition, make sure you remove that buffer.get call earlier - it'll make the rest of the reads from plane 0 not work right, which may be your current problem. Once you read plane 0 once, .remaining() will return 0.
The YUV image has 3 planes, and unless you've checked that the underlying format is actually NV21, you shouldn't blindly assume that., or assume that the row stride is equal to width.
To be safe, you need to look at both row and pixel strides when copying the three planes into the semiplanar byte[].
If you're willing to move your processing over to C++ you could do the conversion like
cv::cvtColor((cv::_InputArray)mNV, (cv::_OutputArray)rgba, CV_YUV2RGBA_NV12, 0);
This will store the image as an RGBA mat and you can save it as normal from there.
My application overrides the onPreviewFrame callback to pass the current camera frame to a webrtc native function. This works perfectly, however I want to be able to switch to sending a static frame instead of video, if that option has been selected in my app.
So far I have created a YUV NV21 image, which I am storing in the assets dir. All attempts to pass that frame down to the native function have resulted in purple/green stripes rather than the actual image.
This is what I have so far;
#Override
public void onPreviewFrame(byte[] data, Camera camera) {
previewBufferLock.lock();
if (mFrameProvider.isEnabled()) {
mFrameProvider.overwriteWithFrame(data, expectedFrameSize);
}
if (isCaptureRunning) {
if (data.length == expectedFrameSize) {
ProvideCameraFrame(data, expectedFrameSize, context);
cameraUtils.addCallbackBuffer(camera, data);
}
}
previewBufferLock.unlock();
}
#Override
public byte[] overwriteWithPreviewFrame(byte[] data, int expectedFrameSize) {
if (mFrameData == null) {
loadPreviewFrame();
}
for (int i=0; i < expectedFrameSize; i++) {
if (i < mFrameData.length) {
data[i] = mFrameData[i];
}
}
return data;
}
And
private void loadPreviewFrame() {
try {
InputStream open = mContext.getResources().getAssets().open(PREVIEW_FRAME_FILE);
mFrameData = IOUtils.toByteArray(open);
open.close();
} catch (Exception e) {
Log.e("", "", e);
}
}
I have tried converting the image to a bitmap too. So the question is how can I open a YUV frame from assets and convert it into a suitable format to pass to the native methods.
Results in the following output;
Right after a long fight with Android API I have managed to get this working.
There were two issues that caused the green/purple output;
Loss of data: the generated YUV frame was larger than the original preview frame at the same resolution, so the data being passed down to the native code was missing around 30% of its image data.
Wrong resolution: the native code required the resolution of the preview frame and not the camera.
Below is a working solution for anyone who wishes to add a static frame;
so updated code:
#Override
public byte[] getPreviewFrameData(int width, int height) {
if (mPreviewFrameData == null) {
loadPreviewFrame(width, height);
}
return mPreviewFrameData;
}
private void loadPreviewFrame(int width, int height) {
try {
Bitmap previewImage = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.frame);
Bitmap resizedPreviewImage = Bitmap.createScaledBitmap(previewImage, width, height, false);
BitmapConverter bitmapConverter = new BitmapConverter();
mPreviewFrameData = bitmapConverter.convertToNV21(resizedPreviewImage);
} catch (Exception e) {
Log.e("DisabledCameraFrameProvider", "Failed to loadPreviewFrame");
}
}
class BitmapConverter {
byte [] convertToNV21(Bitmap bitmap) {
int inputWidth = bitmap.getWidth();
int inputHeight = bitmap.getHeight();
int [] argb = new int[inputWidth * inputHeight];
bitmap.getPixels(argb, 0, inputWidth, 0, 0, inputWidth, inputHeight);
byte [] yuv = new byte[inputWidth*inputHeight*3/2];
encodeYUV420SP(yuv, argb, inputWidth, inputHeight);
bitmap.recycle();
return yuv;
}
void encodeYUV420SP(byte[] yuv420sp, int[] argb, int width, int height) {
final int frameSize = width * height;
int yIndex = 0;
int uvIndex = frameSize;
int R, G, B, Y, U, V;
int index = 0;
for (int j = 0; j < height; j++) {
for (int i = 0; i < width; i++) {
R = (argb[index] & 0xff0000) >> 16;
G = (argb[index] & 0xff00) >> 8;
B = (argb[index] & 0xff);
Y = ( ( 66 * R + 129 * G + 25 * B + 128) >> 8) + 16;
U = ( ( -38 * R - 74 * G + 112 * B + 128) >> 8) + 128;
V = ( ( 112 * R - 94 * G - 18 * B + 128) >> 8) + 128;
yuv420sp[yIndex++] = (byte) ((Y < 0) ? 0 : ((Y > 255) ? 255 : Y));
if (j % 2 == 0 && index % 2 == 0) {
yuv420sp[uvIndex++] = (byte)((V<0) ? 0 : ((V > 255) ? 255 : V));
yuv420sp[uvIndex++] = (byte)((U<0) ? 0 : ((U > 255) ? 255 : U));
}
index ++;
}
}
}
}
Then finally in your callback;
public void onPreviewFrame(byte[] data, Camera camera) {
byte[] bytes = data;
if (!mProvider.isVideoEnabled()) {
Camera.Size previewSize = camera.getParameters().getPreviewSize();
bytes = mProvider.getPreviewFrameData(previewSize.width, previewSize.height);
}
ProvideCameraFrame(bytes, bytes.length, context);
}
The key was to scale the image to the camera preview size and convert the image to YUV colour space.
I want to print some text with image which is reside on my android phone to Bluetooth printer but text is successfully printed and image is not printed on paper.
I using following code:
public class SendingdataActivity extends Activity {
/** Called when the activity is first created. */
private BluetoothAdapter mBluetoothAdapter = null;
static final UUID MY_UUID =
UUID.fromString("fa87c0d0-afac-11de-8a39-0800200c9a66");
static String address = "50:C3:00:00:00:00";
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (mBluetoothAdapter == null) {
Toast.makeText(this,
"Bluetooth is not available.",
Toast.LENGTH_LONG).show();
finish();
return;
}
if (!mBluetoothAdapter.isEnabled()) {
Toast.makeText(this,
"Please enable your BT and re-run this program.",
Toast.LENGTH_LONG).show();
finish();
return;
}
final SendData sendData = new SendData();
Button sendButton = (Button) findViewById(R.id.send);
sendButton.setOnClickListener(new OnClickListener() {
public void onClick(View view) {
sendData.sendMessage();
}
});
}
class SendData extends Thread {
private BluetoothDevice device = null;
private BluetoothSocket btSocket = null;
private OutputStream outStream = null;
public SendData(){
device = mBluetoothAdapter.getRemoteDevice(address);
try
{
btSocket = device.createRfcommSocketToServiceRecord(MY_UUID);
}
catch (Exception e) {
// TODO: handle exception
}
mBluetoothAdapter.cancelDiscovery();
try {
btSocket.connect();
} catch (IOException e) {
try {
btSocket.close();
} catch (IOException e2) {
}
}
Toast.makeText(getBaseContext(), "Connected to " + device.getName(),
Toast.LENGTH_SHORT).show();
try {
outStream = btSocket.getOutputStream();
} catch (IOException e) {
}
}
public void sendMessage()
{
try {
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
Bitmap bm = BitmapFactory.decodeResource(getResources(), R.drawable.white);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
bm.compress(Bitmap.CompressFormat.JPEG, 100,baos); //bm is the bitmap object
byte[] b = baos.toByteArray();
Toast.makeText(getBaseContext(), String.valueOf(b.length), Toast.LENGTH_SHORT).show();
outStream.write(b);
outStream.flush();
} catch (IOException e) {
}
}
}
}
This code works and print only text on paper.
Thanks.
After long time I am created an app to print image on bluetooth printer by using following steps:
1. Connect your Bluetooth device with Android phone
2. Call senddatatodevice() method when you want to send data on printer
3. code executed within senddatatodevice() is like bellow.
private static void senddatatodevice() {
// TODO Auto-generated method stub
try {
//base64 image string
String sig="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wAMAALAEAAABAA8nAQBsbHVuAAAAAAAAAAAAADhCSU0D7QAAAAAAEAEsAAAAAQABASwAAAABAAE4QklNBCYAAAAAAA4AAAAAAAAAAAAAP4AAADhCSU0EDQAAAAAABAAAAHg4QklNBBkAAAAAAAQAAAAeOEJJTQPzAAAAAAAJAAAAAAAAAAABADhCSU0ECgAAAAAAAQAAOEJJTScQAAAAAAAKAAEAAAAAAAAAAjhCSU0D9QAAAAAASAAvZmYAAQBsZmYABgAAAAAAAQAvZmYAAQChmZoABgAAAAAAAQAyAAAAAQBaAAAABgAAAAAAAQA1AAAAAQAtAAAABgAAAAAAAThCSU0D+AAAAAAAcAAA/////////////////////////////wPoAAAAAP////////////////////////////8D6AAAAAD/////////////////////////////A+gAAAAA/////////////////////////////wPoAAA4QklNBAAAAAAAAAIAAThCSU0EAgAAAAAACAAAAAAAAAAAOEJJTQQwAAAAAAAEAQEBAThCSU0ELQAAAAAABgABAAAABThCSU0ECAAAAAAAEAAAAAEAAAJAAAACQAAAAAA4QklNBB4AAAAAAAQAAAAAOEJJTQQaAAAAAANJAAAABgAAAAAAAAAAAAAAMAAAAHEAAAAKAFUAbgB0AGkAdABsAGUAZAAtADEAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAHEAAAAwAAAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAEAAAAAEAAAAAAABudWxsAAAAAgAAAAZib3VuZHNPYmpjAAAAAQAAAAAAAFJjdDEAAAAEAAAAAFRvcCBsb25nAAAAAAAAAABMZWZ0bG9uZwAAAAAAAAAAQnRvbWxvbmcAAAAwAAAAAFJnaHRsb25nAAAAcQAAAAZzbGljZXNWbExzAAAAAU9iamMAAAABAAAAAAAFc2xpY2UAAAASAAAAB3NsaWNlSURsb25nAAAAAAAAAAdncm91cElEbG9uZwAAAAAAAAAGb3JpZ2luZW51bQAAAAxFU2xpY2VPcmlnaW4AAAANYXV0b0dlbmVyYXRlZAAAAABUeXBlZW51bQAAAApFU2xpY2VUeXBlAAAAAEltZyAA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PtT8Sh2RM+sa2F8+Pqbd6LkYuNlV+lk0svrOuyxoe3/NeCipJKQ42HiYjPTxKK8esmSypjWCf6rA1CZ0npdd/wBprw6GXzPqtqYHz4+oG71bSSUpJJJJT//ZOEJJTQQhAAAAAABVAAAAAQEAAAAPAEEAZABvAGIAZQAgAFAAaABvAHQAbwBzAGgAbwBwAAAAEwBBAGQAbwBiAGUAIABQAGgAbwB0AG8AcwBoAG8AcAAgAEMAUwAzAAAAAQA4QklNBAYAAAAAAAcACAEBAAMBAP/hD89odHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDQuMS1jMDM2IDQ2LjI3NjcyMCwgTW9uIEZlYiAxOSAyMDA3IDIyOjQwOjA4ICAgICAgICAiPiA8cmRmOlJERiB4bWxuczpyZGY9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkvMDIvMjItcmRmLXN5bnRheC1ucyMiPiA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIiB4bWxuczpkYz0iaHR0cDovL3B1cmwub3JnL2RjL2VsZW1lbnRzLzEuMS8iIHhtbG5zOnhhcD0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wLyIgeG1sbnM6eGFwTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczpwaG90b3Nob3A9Imh0dHA6Ly9ucy5hZG9iZS5jb20vcGhvdG9zaG9wLzEuMC8iIHhtbG5zOnRpZmY9Imh0dHA6Ly9ucy5hZG9iZS5jb20vdGlmZi8xLjAvIiB4bWxuczpleGlmPSJodHRwOi8vbnMuYWRvYmUuY29tL2V4aWYvMS4wLyIgZGM6Zm9ybWF0PSJpbWFnZS9qcGVnIiB4YXA6Q3JlYXRvclRvb2w9IkFkb2JlIFBob3Rvc2hvcCBDUzMgV2luZG93cyIgeGFwOkNyZWF0ZURhdGU9IjIwMTQtMDItMjVUMTI6MjQ6MzYrMDU6MzAiIHhhcDpNb2RpZnlEYXRlPSIyMDE0LTAyLTI1VDEyOjI0OjM2KzA1OjMwIiB4YXA6TWV0YWRhdGFEYXRlPSIyMDE0LTAyLTI1VDEyOjI0OjM2KzA1OjMwIiB4YXBNTTpEb2N1bWVudElEPSJ1dWlkOkQ3MkUyOTYyRTM5REUzMTFBQ0QxOUQzNUM5MzAwREIzIiB4YXBNTTpJbnN0YW5jZUlEPSJ1dWlkOkQ4MkUyOTYyRTM5REUzMTFBQ0QxOUQzNUM5MzAwREIzIiBwaG90b3Nob3A6Q29sb3JNb2RlPSIzIiBwaG90b3Nob3A6SUNDUHJvZmlsZT0ic1JHQiBJRUM2MTk2Ni0yLjEiIHBob3Rvc2hvcDpIaXN0b3J5PSIiIHRpZmY6T3JpZW50YXRpb249IjEiIHRpZmY6WFJlc29sdXRpb249IjMwMDAwMDAvMTAwMDAiIHRpZmY6WVJlc29sdXRpb249IjMwMDAwMDAvMTAwMDAiIHRpZmY6UmVzb2x1dGlvblVuaXQ9IjIiIHRpZmY6TmF0aXZlRGlnZXN0PSIyNTYsMjU3LDI1OCwyNTksMjYyLDI3NCwyNzcsMjg0LDUzMCw1MzEsMjgyLDI4MywyOTYsMzAxLDMxOCwzMTksNTI5LDUzMiwzMDYsMjcwLDI3MSwyNzIsMzA1LDMxNSwzMzQzMjs1NzYxNzA0NkUxNUE0RThGODE1NzcxNTIyREJFQUIzQiIgZXhpZjpQaXhlbFhEaW1lbnNpb249IjExMyIgZXhpZjpQaXhlbFlEaW1lbnNpb249IjQ4IiBleGlmOkNvbG9yU3BhY2U9IjEiIGV4aWY6TmF0aXZlRGlnZXN0PSIzNjg2NCw0MDk2MCw0MDk2MSwzNzEyMSwzNzEyMiw0MDk2Miw0MDk2MywzNzUxMCw0MDk2NCwzNjg2NywzNjg2OCwzMzQzNCwzMzQzNywzNDg1MCwzNDg1MiwzNDg1NSwzNDg1NiwzNzM3NywzNzM3OCwzNzM3OSwzNzM4MCwzNzM4MSwzNzM4MiwzNzM4MywzNzM4NCwzNzM4NSwzNzM4NiwzNzM5Niw0MTQ4Myw0MTQ4NCw0MTQ4Niw0MTQ4Nyw0MTQ4OCw0MTQ5Miw0MTQ5Myw0MTQ5NSw0MTcyOCw0MTcyOSw0MTczMCw0MTk4NSw0MTk4Niw0MTk4Nyw0MTk4OCw0MTk4OSw0MTk5MCw0MTk5MSw0MTk5Miw0MTk5Myw0MTk5NCw0MTk5NSw0MTk5Niw0MjAxNiwwLDIsNCw1LDYsNyw4LDksMTAsMTEsMTIsMTMsMTQsMTUsMTYsMTcsMTgsMjAsMjIsMjMsMjQsMjUsMjYsMjcsMjgsMzA7RjY5RTI3MkY2NzczN0M2MkI3MjNCQTdFQzg4QTM3OEIiPiA8eGFwTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0idXVpZDozRThEMUZDRUUwOURFMzExQUNEMTlEMzVDOTMwMERCMyIgc3RSZWY6ZG9jdW1lbnRJRD0idXVpZDozRThEMUZDRUUwOURFMzExQUNEMTlEMzVDOTMwMERCMyIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICA8P3hwYWNrZXQgZW5kPSJ3Ij8+/+IMWElDQ19QUk9GSUxFAAEBAAAMSExpbm8CEAAAbW50clJHQiBYWVogB84AAgAJAAYAMQAAYWNzcE1TRlQAAAAASUVDIHNSR0IAAAAAAAAAAAAAAAEAAPbWAAEAAAAA0y1IUCAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARY3BydAAAAVAAAAAzZGVzYwAAAYQAAABsd3RwdAAAAfAAAAAUYmtwdAAAAgQAAAAUclhZWgAAAhgAAAAUZ1hZWgAAAiwAAAAUYlhZWgAAAkAAAAAUZG1uZAAAAlQA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";
byte[] imageAsBytes = android.util.Base64
.decode(sig, android.util.Base64.DEFAULT);
btMap = BitmapFactory.decodeByteArray(imageAsBytes, 0,
imageAsBytes.length);
Bitmap bitmapOrg = resizeImage(btMap, 384,
150);// Bit
byte[] sendbuf = StartBmpToPrintCode(bitmapOrg, 0);
/*byte[] a=t2.getBytes();
byte[] combined = new byte[a.length + sendbuf.length];
System.arraycopy(a,0,combined,0 ,a.length);
System.arraycopy(sendbuf,0,combined,a.length,sendbuf.length);*/
mbtOutputStream = mbtSocket.getOutputStream();
mbtOutputStream.write(sendbuf);
mbtOutputStream.flush();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static Bitmap resizeImage(Bitmap bitmap, int w, int h) {
Bitmap BitmapOrg = bitmap;
int width = BitmapOrg.getWidth();
int height = BitmapOrg.getHeight();
int newWidth = w;
int newHeight = h;
float scaleWidth = ((float) newWidth) / width;
float scaleHeight = ((float) newHeight) / height;
Matrix matrix = new Matrix();
matrix.postScale(scaleWidth, scaleWidth);
Bitmap resizedBitmap = Bitmap.createBitmap(BitmapOrg, 10,10, width,
height, matrix, true);
return resizedBitmap;
}
private static byte[] StartBmpToPrintCode(Bitmap bitmap, int t) {
byte temp = 0;
int j = 7;
int start = 0;
if (bitmap != null) {
int mWidth = bitmap.getWidth();
int mHeight = bitmap.getHeight();
int[] mIntArray = new int[mWidth * mHeight];
byte[] data = new byte[mWidth * mHeight];
bitmap.getPixels(mIntArray, 0, mWidth, 0, 0, mWidth, mHeight);
encodeYUV420SP(data, mIntArray, mWidth, mHeight, t);
byte[] result = new byte[mWidth * mHeight / 8];
for (int i = 0; i < mWidth * mHeight; i++) {
temp = (byte) ((byte) (data[i] << j) + temp);
j--;
if (j < 0) {
j = 7;
}
if (i % 8 == 7) {
result[start++] = temp;
temp = 0;
}
}
if (j != 7) {
result[start++] = temp;
}
int aHeight = 24 - mHeight % 24;
byte[] add = new byte[aHeight * 48];
byte[] nresult = new byte[mWidth * mHeight / 8 + aHeight * 48];
System.arraycopy(result, 0, nresult, 0, result.length);
System.arraycopy(add, 0, nresult, result.length, add.length);
byte[] byteContent = new byte[(mWidth / 8 + 4)
* (mHeight + aHeight)];// ´òÓ¡Êý×é
byte[] bytehead = new byte[4];// ÿÐдòÓ¡Í·
bytehead[0] = (byte) 0x1f;
bytehead[1] = (byte) 0x10;
bytehead[2] = (byte) (mWidth / 8);
bytehead[3] = (byte) 0x00;
for (int index = 0; index < mHeight + aHeight; index++) {
System.arraycopy(bytehead, 0, byteContent, index * 52, 4);
System.arraycopy(nresult, index * 48, byteContent,
index * 52 + 4, 48);
}
return byteContent;
}
return null;
}
public static void encodeYUV420SP(byte[] yuv420sp, int[] rgba, int width,
int height, int t) {
final int frameSize = width * height;
int[] U, V;
U = new int[frameSize];
V = new int[frameSize];
final int uvwidth = width / 2;
int r, g, b, y, u, v;
int bits = 8;
int index = 0;
int f = 0;
for (int j = 0; j < height; j++) {
for (int i = 0; i < width; i++) {
r = (rgba[index] & 0xff000000) >> 24;
g = (rgba[index] & 0xff0000) >> 16;
b = (rgba[index] & 0xff00) >> 8;
// rgb to yuv
y = ((66 * r + 129 * g + 25 * b + 128) >> 8) + 16;
u = ((-38 * r - 74 * g + 112 * b + 128) >> 8) + 128;
v = ((112 * r - 94 * g - 18 * b + 128) >> 8) + 128;
// clip y
// yuv420sp[index++] = (byte) ((y < 0) ? 0 : ((y > 255) ? 255 :
// y));
byte temp = (byte) ((y < 0) ? 0 : ((y > 255) ? 255 : y));
if (t == 0) {
yuv420sp[index++] = temp > 0 ? (byte) 1 : (byte) 0;
} else {
yuv420sp[index++] = temp > 0 ? (byte) 0 : (byte) 1;
}
// {
// if (f == 0) {
// yuv420sp[index++] = 0;
// f = 1;
// } else {
// yuv420sp[index++] = 1;
// f = 0;
// }
// }
}
}
f = 0;
}