【发布时间】:2016-02-02 06:36:36
【问题描述】:
我正在尝试创建一个蓝牙相机应用程序,其中 2 台设备通过蓝牙连接并单击设备 1 上的 开始按钮,它应该 在设备 2 上启动相机 和相机预览应显示在两个设备上。 当用户在设备 1 上点击 capture 时,设备 2 应拍摄照片。
我正在使用蓝牙聊天示例代码,我可以发出启动命令,但我不知道如何显示来自相机的预览数据流(字节 [])并在其他设备上显示预览
我尝试将 byte[] 转换为位图,但位图为空。
在设备 1 上预览数据
camera.setPreviewCallback(new Camera.PreviewCallback() {
@Override
public void onPreviewFrame(byte[] data, Camera camera) {
//Here I am writing data through connected channel
//tried to convert byte[] to bitmap here also but its showing null
BitmapFactory.Options options = new BitmapFactory.Options();
bitmap = BitmapFactory.decodeByteArray(image, 0, image.length, options);
//setting bitmap to imageview but bitmap itself is null
}
在另一台设备上,它连续读取设备 1 写入的数据,处理程序将读取的数据传递到 byte[] 中,这个 byte[] 我试图在 Surfaceview 或 imageview 中显示,但两者都不起作用
蓝牙相机片段
public class BluetoothCameraFragment extends Fragment implements SurfaceHolder.Callback{
TextView testView;
Camera camera;
SurfaceView surfaceView;
SurfaceHolder surfaceHolder;
Camera.PictureCallback rawCallback;
Camera.ShutterCallback shutterCallback;
Camera.PictureCallback jpegCallback;
private final String tag = "tagg";
Button start, stop, capture;
private static final String TAG = "BluetoothCamera";
private static final int REQUEST_CONNECT_DEVICE_SECURE = 1;
private static final int REQUEST_CONNECT_DEVICE_INSECURE = 2;
private static final int REQUEST_ENABLE_BT = 3;
private ListView mConversationView;
private EditText mOutEditText;
private Button mSendButton;
private ImageView imageview;
private String mConnectedDeviceName = null;
private ArrayAdapter<String> mConversationArrayAdapter;
private StringBuffer mOutStringBuffer;
private BluetoothAdapter mBluetoothAdapter = null;
private BluetoothCameraManager mCameraService = null;
private boolean isCameraRunning = false;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (mBluetoothAdapter == null) {
FragmentActivity activity = getActivity();
Toast.makeText(activity, "Bluetooth is not available", Toast.LENGTH_LONG).show();
activity.finish();
}
}
@Override
public void onStart() {
super.onStart();
if (!mBluetoothAdapter.isEnabled()) {
Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableIntent, REQUEST_ENABLE_BT);
} else if (mCameraService == null) {
setup();
}
}
@Override
public void onDestroy() {
super.onDestroy();
if (mCameraService != null) {
mCameraService.stop();
}
}
@Override
public void onResume() {
super.onResume();
if (mCameraService != null) {
if (mCameraService.getState() == BluetoothCameraManager.STATE_NONE) {
mCameraService.start();
}
}
}
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_bluetooth_camera, container, false);
}
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
mSendButton = (Button) view.findViewById(R.id.button_send);
stop = (Button) view.findViewById(R.id.stop);
capture = (Button) view.findViewById(R.id.capture);
imageview = (ImageView) view.findViewById(R.id.previewImage);
}
private void setup() {
Log.d(TAG, "setup()");
mSendButton.setOnClickListener(new Button.OnClickListener()
{
public void onClick(View arg0) {
sendMessage("start-camera".getBytes());
}
});
View view = getView();
if (null != view) {
stop = (Button)view.findViewById(R.id.stop);
capture = (Button) view.findViewById(R.id.capture);
}
stop.setOnClickListener(new Button.OnClickListener()
{
public void onClick(View arg0) {
sendMessage("stop-camera".getBytes());
}
});
capture.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
sendMessage("take-picture".getBytes());
}
});
surfaceView = (SurfaceView)view.findViewById(R.id.surfaceview);
surfaceHolder = surfaceView.getHolder();
surfaceHolder.addCallback(this);
surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
rawCallback = new Camera.PictureCallback() {
public void onPictureTaken(byte[] data, Camera camera) {
Log.d("Log", "onPictureTaken - raw");
}
};
shutterCallback = new Camera.ShutterCallback() {
public void onShutter() {
Log.i("Log", "onShutter'd");
}
};
jpegCallback = new Camera.PictureCallback() {
public void onPictureTaken(byte[] data, Camera camera) {
FileOutputStream outStream = null;
try {
outStream = new FileOutputStream(String.format(
"/sdcard/%d.jpg", System.currentTimeMillis()));
outStream.write(data);
outStream.close();
Log.d("Log", "onPictureTaken - wrote bytes: " + data.length);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
}
Log.d("Log", "onPictureTaken - jpeg");
}
};
mCameraService = new BluetoothCameraManager(getActivity(), mHandler);
mOutStringBuffer = new StringBuffer("");
}
private void ensureDiscoverable() {
if (mBluetoothAdapter.getScanMode() !=
BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) {
Intent discoverableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
discoverableIntent.putExtra(BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, 300);
startActivity(discoverableIntent);
}
}
private void sendMessage(byte[] preview) {
if (mCameraService.getState() != BluetoothCameraManager.STATE_CONNECTED) {
Toast.makeText(getActivity(), R.string.not_connected, Toast.LENGTH_SHORT).show();
return;
}
Log.d("sttatt","sending data");
mCameraService.write(preview);
}
private TextView.OnEditorActionListener mWriteListener
= new TextView.OnEditorActionListener() {
public boolean onEditorAction(TextView view, int actionId, KeyEvent event) {
if (actionId == EditorInfo.IME_NULL && event.getAction() == KeyEvent.ACTION_UP) {
String message = view.getText().toString();
}
return true;
}
};
private void setStatus(int resId) {
FragmentActivity activity = getActivity();
if (null == activity) {
return;
}
final ActionBar actionBar = activity.getActionBar();
if (null == actionBar) {
return;
}
actionBar.setSubtitle(resId);
}
private void setStatus(CharSequence subTitle) {
FragmentActivity activity = getActivity();
if (null == activity) {
return;
}
final ActionBar actionBar = activity.getActionBar();
if (null == actionBar) {
return;
}
actionBar.setSubtitle(subTitle);
}
private final Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
FragmentActivity activity = getActivity();
switch (msg.what) {
case Constants.MESSAGE_STATE_CHANGE:
switch (msg.arg1) {
case BluetoothCameraManager.STATE_CONNECTED:
setStatus(getString(R.string.title_connected_to, mConnectedDeviceName));
break;
case BluetoothCameraManager.STATE_CONNECTING:
setStatus(R.string.title_connecting);
break;
case BluetoothCameraManager.STATE_LISTEN:
case BluetoothCameraManager.STATE_NONE:
setStatus(R.string.title_not_connected);
break;
}
break;
case Constants.START_CAMERA_SERVICE:
byte[] readBuf = (byte[]) msg.obj;
String command = new String(readBuf).toString();
Log.d("cammy", "" + command);
if(command.equals("start-camera")){
Log.d("cammy","Startcam");
start_camera();
Toast.makeText(getActivity(),"starting camera",Toast.LENGTH_LONG).show();
}else if(command.equals("stop-camera")){
Log.d("cammy","Stopcam");
stop_camera();
Toast.makeText(getActivity(),"stopping camera",Toast.LENGTH_LONG).show();
}else if(command.equals("take-picture")){
Log.d("cammy","takepic");
captureImage();
Toast.makeText(getActivity(),"Take picture",Toast.LENGTH_LONG).show();
}else {
Log.d("cammy","No trigger");
}
break;
case Constants.STOP_CAMERA:
break;
case Constants.TAKE_PICTURE:
break;
case Constants.MESSAGE_WRITE:
mSendButton.setClickable(false);
Log.d("sttatt","writing data");
break;
case Constants.MESSAGE_READ:
break;
case Constants.MESSAGE_DEVICE_NAME:
mConnectedDeviceName = msg.getData().getString(Constants.DEVICE_NAME);
if (null != activity) {
Toast.makeText(activity, "Connected to "
+ mConnectedDeviceName, Toast.LENGTH_SHORT).show();
}
break;
case Constants.MESSAGE_TOAST:
if (null != activity) {
Toast.makeText(activity, msg.getData().getString(Constants.TOAST),
Toast.LENGTH_SHORT).show();
}
break;
}
}
};
public void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case REQUEST_CONNECT_DEVICE_SECURE:
if (resultCode == Activity.RESULT_OK) {
connectDevice(data, true);
}
break;
case REQUEST_CONNECT_DEVICE_INSECURE:
if (resultCode == Activity.RESULT_OK) {
connectDevice(data, false);
}
break;
case REQUEST_ENABLE_BT:
if (resultCode == Activity.RESULT_OK) {
setup();
} else {
Log.d(TAG, "BT not enabled");
Toast.makeText(getActivity(), R.string.bt_not_enabled_leaving,
Toast.LENGTH_SHORT).show();
getActivity().finish();
}
}
}
private void connectDevice(Intent data, boolean secure) {
String address = data.getExtras()
.getString(DeviceListActivity.EXTRA_DEVICE_ADDRESS);
BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
mCameraService.connect(device, secure);
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.bluetooth_camera, menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.secure_connect_scan: {
Intent serverIntent = new Intent(getActivity(), DeviceListActivity.class);
startActivityForResult(serverIntent, REQUEST_CONNECT_DEVICE_SECURE);
return true;
}
case R.id.insecure_connect_scan: {
Intent serverIntent = new Intent(getActivity(), DeviceListActivity.class);
startActivityForResult(serverIntent, REQUEST_CONNECT_DEVICE_INSECURE);
return true;
}
case R.id.discoverable: {
ensureDiscoverable();
return true;
}
}
return false;
}
private void captureImage() {
camera.takePicture(shutterCallback, rawCallback, jpegCallback);
}
private void start_camera(){
try{
camera = Camera.open();
isCameraRunning=true;
}catch(RuntimeException e){
Log.e(tag, "init_camera: " + e);
return;
}
Camera.Parameters param;
param = camera.getParameters();
param.setPreviewFrameRate(20);
param.setPreviewSize(176, 144);
camera.setParameters(param);
try {
camera.setPreviewDisplay(surfaceHolder);
camera.setPreviewCallback(new Camera.PreviewCallback() {
@Override
public void onPreviewFrame(byte[] data, Camera camera) {
}
});
camera.startPreview();
} catch (Exception e) {
Log.e(tag, "init_camera: " + e);
return;
}
}
private void stop_camera()
{
camera.stopPreview();
camera.release();
}
public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) {
}
public void surfaceCreated(SurfaceHolder holder) {
}
public void surfaceDestroyed(SurfaceHolder holder) {
}
}
蓝牙管理器
public class BluetoothCameraManager {
private static final String TAG = "rolf";
private static final String NAME_SECURE = "BluetoothCameraSecure";
private static final String NAME_INSECURE = "BluetoothCameraInsecure";
private static final UUID MY_UUID_SECURE =
UUID.fromString("fa87c0d0-afac-11de-8a39-0800200c9a66");
private static final UUID MY_UUID_INSECURE =
UUID.fromString("8ce255c0-200a-11e0-ac64-0800200c9a66");
private final BluetoothAdapter mAdapter;
private final Handler mHandler;
private AcceptThread mSecureAcceptThread;
private AcceptThread mInsecureAcceptThread;
private ConnectThread mConnectThread;
private ConnectedThread mConnectedThread;
private int mState;
public static final int STATE_NONE = 0;
public static final int STATE_LISTEN = 1;
public static final int STATE_CONNECTING = 2;
public static final int STATE_CONNECTED = 3;
public BluetoothCameraManager(Context context, Handler handler) {
mAdapter = BluetoothAdapter.getDefaultAdapter();
mState = STATE_NONE;
mHandler = handler;
}
private synchronized void setState(int state) {
Log.d(TAG, "setState() " + mState + " -> " + state);
mState = state;
mHandler.obtainMessage(Constants.MESSAGE_STATE_CHANGE, state, -1).sendToTarget();
}
public synchronized int getState() {
return mState;
}
public synchronized void start() {
Log.d(TAG, "start");
if (mConnectThread != null) {
mConnectThread.cancel();
mConnectThread = null;
}
if (mConnectedThread != null) {
mConnectedThread.cancel();
mConnectedThread = null;
}
setState(STATE_LISTEN);
if (mSecureAcceptThread == null) {
mSecureAcceptThread = new AcceptThread(true);
mSecureAcceptThread.start();
}
if (mInsecureAcceptThread == null) {
mInsecureAcceptThread = new AcceptThread(false);
mInsecureAcceptThread.start();
}
}
public synchronized void connect(BluetoothDevice device, boolean secure) {
Log.d(TAG, "connect to: " + device);
if (mState == STATE_CONNECTING) {
if (mConnectThread != null) {
mConnectThread.cancel();
mConnectThread = null;
}
}
if (mConnectedThread != null) {
mConnectedThread.cancel();
mConnectedThread = null;
}
mConnectThread = new ConnectThread(device, secure);
mConnectThread.start();
setState(STATE_CONNECTING);
}
public synchronized void connected(BluetoothSocket socket, BluetoothDevice
device, final String socketType) {
Log.d(TAG, "connected, Socket Type:" + socketType);
if (mConnectThread != null) {
mConnectThread.cancel();
mConnectThread = null;
}
if (mConnectedThread != null) {
mConnectedThread.cancel();
mConnectedThread = null;
}
if (mSecureAcceptThread != null) {
mSecureAcceptThread.cancel();
mSecureAcceptThread = null;
}
if (mInsecureAcceptThread != null) {
mInsecureAcceptThread.cancel();
mInsecureAcceptThread = null;
}
mConnectedThread = new ConnectedThread(socket, socketType);
mConnectedThread.start();
Message msg = mHandler.obtainMessage(Constants.MESSAGE_DEVICE_NAME);
Bundle bundle = new Bundle();
bundle.putString(Constants.DEVICE_NAME, device.getName());
msg.setData(bundle);
mHandler.sendMessage(msg);
setState(STATE_CONNECTED);
}
public synchronized void stop() {
Log.d(TAG, "stop");
if (mConnectThread != null) {
mConnectThread.cancel();
mConnectThread = null;
}
if (mConnectedThread != null) {
mConnectedThread.cancel();
mConnectedThread = null;
}
if (mSecureAcceptThread != null) {
mSecureAcceptThread.cancel();
mSecureAcceptThread = null;
}
if (mInsecureAcceptThread != null) {
mInsecureAcceptThread.cancel();
mInsecureAcceptThread = null;
}
setState(STATE_NONE);
}
public void write(byte[] out) {
ConnectedThread r;
synchronized (this) {
if (mState != STATE_CONNECTED) return;
r = mConnectedThread;
}
r.write(out);
}
private void connectionFailed() {
Message msg = mHandler.obtainMessage(Constants.MESSAGE_TOAST);
Bundle bundle = new Bundle();
bundle.putString(Constants.TOAST, "Unable to connect device");
msg.setData(bundle);
mHandler.sendMessage(msg);
BluetoothCameraManager.this.start();
}
private void connectionLost() {
Message msg = mHandler.obtainMessage(Constants.MESSAGE_TOAST);
Bundle bundle = new Bundle();
bundle.putString(Constants.TOAST, "Device connection was lost");
msg.setData(bundle);
mHandler.sendMessage(msg);
BluetoothCameraManager.this.start();
}
private class AcceptThread extends Thread {
private final BluetoothServerSocket mmServerSocket;
private String mSocketType;
public AcceptThread(boolean secure) {
BluetoothServerSocket tmp = null;
mSocketType = secure ? "Secure" : "Insecure";
try {
if (secure) {
tmp = mAdapter.listenUsingRfcommWithServiceRecord(NAME_SECURE,
MY_UUID_SECURE);
} else {
tmp = mAdapter.listenUsingInsecureRfcommWithServiceRecord(
NAME_INSECURE, MY_UUID_INSECURE);
}
} catch (IOException e) {
Log.e(TAG, "Socket Type: " + mSocketType + "listen() failed", e);
}
mmServerSocket = tmp;
}
public void run() {
Log.d(TAG, "Accept thread: " + mSocketType +
"BEGIN mAcceptThread" + this);
setName("AcceptThread" + mSocketType);
BluetoothSocket socket = null;
while (mState != STATE_CONNECTED) {
try {
socket = mmServerSocket.accept();
} catch (IOException e) {
Log.e(TAG, "Socket Type: " + mSocketType + "accept() failed", e);
break;
}
if (socket != null) {
synchronized (BluetoothCameraManager.this) {
switch (mState) {
case STATE_LISTEN:
case STATE_CONNECTING:
connected(socket, socket.getRemoteDevice(),
mSocketType);
break;
case STATE_NONE:
case STATE_CONNECTED:
try {
socket.close();
} catch (IOException e) {
Log.e(TAG, "Could not close unwanted socket", e);
}
break;
}
}
}
}
Log.i(TAG, "END mAcceptThread, socket Type: " + mSocketType);
}
public void cancel() {
Log.d(TAG, "Socket Type" + mSocketType + "cancel " + this);
try {
mmServerSocket.close();
} catch (IOException e) {
Log.e(TAG, "Socket Type" + mSocketType + "close() of server failed", e);
}
}
}
private class ConnectThread extends Thread {
private final BluetoothSocket mmSocket;
private final BluetoothDevice mmDevice;
private String mSocketType;
public ConnectThread(BluetoothDevice device, boolean secure) {
mmDevice = device;
BluetoothSocket tmp = null;
mSocketType = secure ? "Secure" : "Insecure";
try {
if (secure) {
tmp = device.createRfcommSocketToServiceRecord(
MY_UUID_SECURE);
} else {
tmp = device.createInsecureRfcommSocketToServiceRecord(
MY_UUID_INSECURE);
}
} catch (IOException e) {
Log.e(TAG, "Socket Type: " + mSocketType + "create() failed", e);
}
mmSocket = tmp;
}
public void run() {
Log.i(TAG, "BEGIN mConnectThread SocketType:" + mSocketType);
setName("ConnectThread" + mSocketType);
mAdapter.cancelDiscovery();
try {
mmSocket.connect();
} catch (IOException e) {
try {
mmSocket.close();
} catch (IOException e2) {
Log.e(TAG, "unable to close() " + mSocketType +
" socket during connection failure", e2);
}
connectionFailed();
return;
}
synchronized (BluetoothCameraManager.this) {
mConnectThread = null;
}
connected(mmSocket, mmDevice, mSocketType);
}
public void cancel() {
try {
mmSocket.close();
} catch (IOException e) {
Log.e(TAG, "close() of connect " + mSocketType + " socket failed", e);
}
}
}
private class ConnectedThread extends Thread {
private final BluetoothSocket mmSocket;
private final InputStream mmInStream;
private final OutputStream mmOutStream;
public ConnectedThread(BluetoothSocket socket, String socketType) {
Log.d(TAG, "create ConnectedThread: " + socketType);
mmSocket = socket;
InputStream tmpIn = null;
OutputStream tmpOut = null;
try {
tmpIn = socket.getInputStream();
tmpOut = socket.getOutputStream();
} catch (IOException e) {
Log.e(TAG, "temp sockets not created", e);
}
mmInStream = tmpIn;
mmOutStream = tmpOut;
}
public void run() {
Log.d(TAG, "BEGIN mConnectedThread");
byte[] buffer = new byte[1024];
int bytes;
while (true) {
try {
bytes = mmInStream.read(buffer);
mHandler.obtainMessage(Constants.START_CAMERA_SERVICE, bytes, -1, buffer)
.sendToTarget();
Log.d(TAG, "Reading");
} catch (IOException e) {
Log.e(TAG, "disconnected", e);
connectionLost();
BluetoothCameraManager.this.start();
break;
}
}
}
public void write(byte[] buffer) {
try {
mmOutStream.write(buffer);
} catch (IOException e) {
Log.e(TAG, "Exception during write", e);
}
}
public void cancel() {
try {
mmSocket.close();
} catch (IOException e) {
Log.e(TAG, "close() of connect socket failed", e);
}
}
}
}
【问题讨论】:
-
恐怕蓝牙的带宽不足以进行视频镜像。但即使在 Wi-Fi 上,原始像素(或位图)也太大而无法传输。查看libstreaming project 的工作库,该库可以通过 IP 将视频流从 Android 摄像头发送到另一个屏幕。
-
预览帧不是位图,这就是您在位图中获得
null的原因。它们通常是 YUV(/总是可能)。 stackoverflow.com/q/4768165/360211 -
@weston 谢谢有什么办法可以将此字节 [] 数据设置为其他表面视图?或者我必须将 byte[] 转换为位图并将其设置为 imageview
-
@AlexCohn 谢谢,我会看看。
-
在我的工作场所使用developer.samsung.com/galaxy#chord做了一些测试项目(仅适用于三星设备)
标签: java android bitmap bluetooth camera