The problem
In cleaning up my code I want to move my Android camera methods to a separate class, in line with what I believe are best practices. After searching all day, I'm still struggling to figure out how to do this exactly. Main problem is that the differences in implementation methods and moving from camera API to camera2 API lead to solutions found online which I can't replicate. Please note that I am quite a beginner in Java and as such, it is probably a very rookie mistake which I can't solve due to the variety of info on the web.
Current code
My main problem is that SurfaceTexture texture = surfaceView.getSurfaceTexture(); in startCamera() says Cannot resolve method 'getSurfaceTexture()' and that previewBuilder.addTarget(texture); complains about addTarget (android.view.surface) in Builder cannot be applied to (android.graphics.SurfaceTexture).
public class CameraView extends TextureView implements TextureView.SurfaceTextureListener{
private Size previewsize;
private CameraDevice cameraDevice;
private CaptureRequest.Builder previewBuilder;
private CameraCaptureSession previewSession;
private final Context context;
public SurfaceView surfaceView;
public TextureView textureView;
public CameraView(Context context, AttributeSet attrs) {
super(context, attrs);
this.context = context;
}
public void onSurfaceTextureAvailable(SurfaceTexture texture, int width, int height) {
// Once the surface is created, simply open a handle to the camera hardware.
openCamera();
}
public void onSurfaceTextureUpdated(SurfaceTexture texture) {
}
public void onSurfaceTextureSizeChanged(SurfaceTexture texture, int width, int height) {
try {
//cameraDevice.setPreviewDisplay(holder);
//cameraDevice.startPreview();
} catch (Exception e){
e.printStackTrace();
}
}
public boolean onSurfaceTextureDestroyed(SurfaceTexture texture) {
// stopPreview();
cameraDevice.close();
return true;
}
public void openCamera()
{
CameraManager manager = (CameraManager) context.getSystemService (Context.CAMERA_SERVICE);
try
{
String cameraId = manager.getCameraIdList()[0];
CameraCharacteristics characteristics=manager.getCameraCharacteristics(cameraId);
StreamConfigurationMap map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
previewsize = map.getOutputSizes(SurfaceTexture.class)[0];
try {
manager.openCamera(cameraId, stateCallback, null);
} catch (SecurityException e){
e.printStackTrace();
}
} catch (Exception e) {
e.printStackTrace();
}
}
private CameraDevice.StateCallback stateCallback = new CameraDevice.StateCallback() {
#Override
public void onOpened(CameraDevice camera) {
cameraDevice = camera;
startCamera();
}
#Override
public void onClosed(CameraDevice camera) {
// nothing
}
#Override
public void onDisconnected(CameraDevice camera) {
}
#Override
public void onError(CameraDevice camera, int error) {
}
};
void startCamera()
{
if (cameraDevice == null || previewsize==null)
{
return;
}
SurfaceTexture texture = surfaceView.getSurfaceTexture();
texture.setDefaultBufferSize(previewsize.getWidth(),previewsize.getHeight());
Surface surface = new Surface(texture);
try
{
// add all the standard stuff to the previewBuilder
previewBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
} catch (Exception e) {}
previewBuilder.addTarget(texture);
try
{
cameraDevice.createCaptureSession(Arrays.asList(surface), new CameraCaptureSession.StateCallback() {
#Override
public void onConfigured(CameraCaptureSession session) {
previewSession = session;
getChangedPreview();
}
#Override
public void onConfigureFailed(CameraCaptureSession session{
}
},null);
} catch (Exception e) {}
}
void getChangedPreview()
{
previewBuilder.set(CaptureRequest.CONTROL_AE_MODE, CameraMetadata.CONTROL_AE_MODE_ON);
HandlerThread thread = new HandlerThread("changed Preview");
thread.start();
Handler handler = new Handler(thread.getLooper());
try
{
previewSession.setRepeatingRequest(previewBuilder.build(), null, handler);
}catch (Exception e){}
}
}
The goal
To keep my code clean and understandable I would like to limit the MainActivity class to switching between views instead of having tons of methods in there. I would like to activate my camera view in my app by switching the following object from INVISIBLE to VISIBLE. Other suggestions are appreciated.
cameraView = (CameraView) findViewById(R.id.camera);
MainActivity.java would then look like:
public class MainActivity extends AppCompatActivity {
private TextView mTextMessage;
private CameraView cameraView;
private MainSurfaceView mGLView;
private TextureView textureView;
private BottomNavigationView.OnNavigationItemSelectedListener mOnNavigationItemSelectedListener
= new BottomNavigationView.OnNavigationItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
switch (item.getItemId()) {
case R.id.navigation_home:
mTextMessage.setText(R.string.title_home);
return true;
case R.id.navigation_dashboard:
mTextMessage.setText(R.string.title_dashboard);
return true;
case R.id.navigation_notifications:
mTextMessage.setText(R.string.title_notifications);
return true;
}
return false;
}
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTextMessage = (TextView) findViewById(R.id.message);
cameraView = (CameraView) findViewById(R.id.camera);
mGLView = (MainSurfaceView) findViewById(R.id.glSurfaceView);
BottomNavigationView navigation = (BottomNavigationView) findViewById(R.id.navigation);
navigation.setOnNavigationItemSelectedListener(mOnNavigationItemSelectedListener);
}
}
Your help is appreciated!
SurfaceTexture texture = surfaceView.getSurfaceTexture(); in startCamera() says Cannot resolve method 'getSurfaceTexture()'
You call the method getSurfaceTexture of surfaceView. surfaceView is a SurfaceView. Let's take a look at the documentation:
https://developer.android.com/reference/android/view/SurfaceView.html
Apparently SurfaceView has no method called "getSurfaceTexture()". However, searching on Google for "getSurfaceTexture() Android" shows us that the method belongs to the TextureView class. Your class CameraView has a field called "textureView", so (I don't know what you want to achieve exactly) can call the method on that field if you want. Additionally your class CameraView is a TextureView itself (do you want that), so you could also just call getSurfaceTexture() if you want to invoke it on the class itself.
previewBuilder.addTarget(texture); complains about addTarget (android.view.surface) in Builder cannot be applied to (android.graphics.SurfaceTexture).
Let's have a look at the documentation again: https://developer.android.com/reference/android/hardware/camera2/CaptureRequest.Builder.html
Apparently CaptureRequest.builder (the type of previewBuilder) has a method called addTarget, but that method only accepts a Surface! You're passing a texture. You probably want to change texture to surface
Related
I am following a tutorial I found at http://www.androidauthority.com/android-game-java-785331/
I just wanted a basic tutorial to get me started making games on android.
Here is the MainActivity.java
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
this.requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(new GameView(this));
}
}
Here is the Class Android Studio is claiming as abstract:
public class MainThread extends Thread {
private SurfaceHolder surfaceHolder;
private GameView gameView;
private boolean running;
public static Canvas canvas;
public MainThread(SurfaceHolder surfaceHolder, GameView gameView) {
super();
this.surfaceHolder = surfaceHolder;
this.gameView = gameView;
}
#Override
public void run() {
while (running) {
canvas = null;
try {
canvas = this.surfaceHolder.lockCanvas();
synchronized(surfaceHolder) {
this.gameView.update();
this.gameView.draw(canvas);
}
} catch (Exception e) {} finally {
if (canvas != null) {
try {
surfaceHolder.unlockCanvasAndPost(canvas);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
public void setRunning(boolean isRunning) {
running = isRunning;
}
}
And Here is the class that is attempting to instantiate the "abstract" class:
public class GameView extends SurfaceView implements SurfaceHolder.Callback {
public MainThread thread;
public GameView(Context context) {
super(context);
getHolder().addCallback(this);
thread = new MainThread(getHolder(), this);
setFocusable(true);
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
thread.setRunning(true);
thread.start();
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
boolean retry = true;
while (retry) {
try {
thread.setRunning(false);
thread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
retry = false;
}
}
public void update() {
}
}
I copied and pasted all the code from the tutorial. The only thing I can think of is the super class? or if there was a button I accidentally clicked when creating the class that made it abstract? I'm not sure what's wrong but I can't continue with the tutorial with errors. If anyone can see what's wrong or can maybe recommend another Android Studio Game tutorial I would very much appreciate it.
It's a problem with the naming. I had the exact same problem and merely changing it to 'GameThread' solved the problem
Alright so I am working on an app that has to take pictures and send them to a server for processing. I need this for some image recognition that will eventually help control a robot. Basically I need to use the android device as a webcam that sends pictures. I figured out the Sockets part but now after fiddling with some code for a few days I ended up with this:
public class MainActivity extends AppCompatActivity {
public static final String dTag = "DBG";
#SuppressWarnings("deprecation")
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button trg = (Button)findViewById(R.id.trigger_btn);
trg.setOnClickListener(new Button.OnClickListener(){
#Override
public void onClick(View v)
{
SurfaceHolder mSurfaceHolder = new SurfaceHolder() {
#Override
public void addCallback(Callback callback) {
}
#Override
public void removeCallback(Callback callback) {
}
#Override
public boolean isCreating() {
return false;
}
#Override
public void setType(int type) {
}
#Override
public void setFixedSize(int width, int height) {
}
#Override
public void setSizeFromLayout() {
}
#Override
public void setFormat(int format) {
}
#Override
public void setKeepScreenOn(boolean screenOn) {
}
#Override
public Canvas lockCanvas() {
return null;
}
#Override
public Canvas lockCanvas(Rect dirty) {
return null;
}
#Override
public void unlockCanvasAndPost(Canvas canvas) {
}
#Override
public Rect getSurfaceFrame() {
return null;
}
#Override
public Surface getSurface() {
return null;
}
};
Camera.PictureCallback mCall = new Camera.PictureCallback()
{
#Override
public void onPictureTaken(byte[] data, Camera camera)
{
ByteArrayInputStream inputStream = new ByteArrayInputStream(data);
Bitmap bitmap = BitmapFactory.decodeStream(inputStream);
ImageView imW = (ImageView)findViewById(R.id.imView);
imW.setImageBitmap(bitmap);
Log.d(dTag, "" + data.length);
}
};
mSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
Camera mCamera;
mCamera = Camera.open();
try {
mCamera.setPreviewDisplay();
mCamera.startPreview();
}
catch(IOException e){
Log.d(dTag, "Cam is null!");
}
mCamera.takePicture(null, null, mCall);
mCamera.stopPreview();
mCamera.release();
}
});
}
Now whenever I press the button I see this in the debug log "D/Camera: app passed NULL surface", I assume this is because of mSurfaceHolder which isn't properly declared. If anyone could point to me what the problem is and how to solve it I would be grateful since I don't have a very good understanding of java and can't seem to find anything that works on the internet.
several problems -
You have no surface view and surface holder must be obtained from the surface view. You just can not create a "new" for this purpose.
You did not pass any surfaceolder in mCamera.setPreviewDisplay(); so system can not decide where to display.
Your method local anonymous inner class is just simply wrong.
Tutorial link: http://examples.javacodegeeks.com/android/core/ui/surfaceview/android-surfaceview-example/
I'm having trouble getting my camera to work (I mean, it does work, but in the meantime I get a NullPointerException). It is thrown at the onSurfaceCreated method:
public class CameraView extends SurfaceView implements SurfaceHolder.Callback {
private SurfaceHolder mHolder;
private Camera mCamera;
public CameraView(Context context, Camera camera) {
super(context);
mCamera = camera;
// Install a SurfaceHolder.Callback so we get notified when the
// underlying surface is created and destroyed.
mHolder = getHolder();
mHolder.addCallback(this);
Log.i("xcamerx", String.valueOf(mCamera));
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
// The Surface has been created, now tell the camera where to draw the preview.
try {
Log.i("xcamera", String.valueOf(mCamera));
Log.i("xcamera", String.valueOf(holder));
mCamera.setPreviewDisplay(holder);
mCamera.startPreview();
} catch (IOException e) {
Log.d("CameraError", "Error starting camera preview: " + e.getMessage());
}
}
public void surfaceDestroyed(SurfaceHolder holder) {
// empty. Take care of releasing the Camera preview in your activity.
}
public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
// If your preview can change or rotate, take care of those events here.
// Make sure to stop the preview before resizing or reformatting it.
if (mHolder.getSurface() == null){
// preview surface does not exist
return;
}
// stop preview before making changes
try {
mCamera.stopPreview();
} catch (Exception e){
// ignore: tried to stop a non-existent preview
}
// set preview size and make any resize, rotate or
// reformatting changes here
// start preview with new settings
try {
mCamera.setPreviewDisplay(mHolder);
mCamera.startPreview();
} catch (Exception e){
Log.d("CameraError", "Error starting camera preview: " + e.getMessage());
}
}
public void pause()
{
mHolder.removeCallback(this);
}
public void resume()
{
mHolder.addCallback(this);
}
}
which is implemented in my SurfaceView. In the activity of this view, I get a Camera instance:
public class CameraActivity extends Activity {
private Camera mCamera;
private CameraView mPreview;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_camera);
// Create an instance of Camera
mCamera = getCameraInstance();
// Create our Preview view and set it as the content of our activity.
mPreview = new CameraView(this, mCamera);
FrameLayout preview = (FrameLayout) findViewById(R.id.camera_view);
preview.addView(mPreview);
}
#Override
protected void onPause() {
// TODO Auto-generated method stub
super.onPause();
mPreview.pause();
}
#Override
protected void onResume() {
// TODO Auto-generated method stub
super.onResume();
mPreview.resume();
}
/** A safe way to get an instance of the Camera object. */
public static Camera getCameraInstance(){
Camera c = null;
try {
c = Camera.open(); // attempt to get a Camera instance
}
catch (Exception e){
// Camera is not available (in use or does not exist)
}
return c; // returns null if camera is unavailable
}
}
which is then passed to the SurfaceView constructor as an argument. In the LogCat, when I print the Camera object (like in the first code block I posted), I get several times a valid Camera object, but now and then there are some nulls which appear for reasons beyond my comprehension. Any ideas?
Hello i have the whole code, but i want to save the snaps automatically and release camera to back preview. I don't know how to do that :/ It's taking the snapshot but don't save neither release camera.
Thanks for the hlep guys!!
package com.velcisribeiro.xcamera;
+imports
public class MainActivity extends Activity {
private Camera cameraObject;
private ShowCamera showCamera;
private ImageView pic;
public static Camera isCameraAvailiable(){
Camera object = null;
try {
object = Camera.open();
}
catch (Exception e){
}
return object;
}
private PictureCallback capturedIt = new PictureCallback() {
#Override
public void onPictureTaken(byte[] data, Camera camera) {
Bitmap bitmap = BitmapFactory.decodeByteArray(data , 0, data .length);
if(bitmap==null){
Toast.makeText(getApplicationContext(), "not taken", Toast.LENGTH_SHORT).show();
}
else
{
Toast.makeText(getApplicationContext(), "taken", Toast.LENGTH_SHORT).show();
}
cameraObject.release();
}
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_camera);
cameraObject = isCameraAvailiable();
showCamera = new ShowCamera(this, cameraObject);
FrameLayout preview = (FrameLayout) findViewById(R.id.camera_preview);
preview.addView(showCamera);
}
public void snapIt(View view){
cameraObject.takePicture(null, null, capturedIt);
}
}
And the other one is:
public class ShowCamera extends SurfaceView implements SurfaceHolder.Callback {
private SurfaceHolder holdMe;
private Camera theCamera;
public ShowCamera(Context context,Camera camera) {
super(context);
theCamera = camera;
holdMe = getHolder();
holdMe.addCallback(this);
}
#Override
public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) {
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
try {
theCamera.setPreviewDisplay(holder);
theCamera.setDisplayOrientation(90);
theCamera.startPreview();
} catch (IOException e) {
}
}
#Override
public void surfaceDestroyed(SurfaceHolder arg0) {
}
}
When I was building my own camera implementation, I just used the code provided by the Zxing library. It works really well and you can easily modify it to do what you'd like:
https://github.com/zxing/zxing
You need to add following two lines in surfaceDestroyed callback for releasing camera.
theCamera.stopPreview();
theCamera.release();
And for saving image change onPictureTaken callback.
public void onPictureTaken(byte[] data, Camera camera) {
Bitmap bitmap = BitmapFactory.decodeByteArray(data , 0, data .length);
if(bitmap==null){
Toast.makeText(getApplicationContext(), "not taken", Toast.LENGTH_SHORT).show();
}
else
{
Toast.makeText(getApplicationContext(), "taken", Toast.LENGTH_SHORT).show();
}
//Add code to save image
cameraObject.release();
}
Also have a look on following URL for better understanding.
http://androidtrainningcenter.blogspot.in/2012/01/how-to-use-android-camera-to-take.html.
I've seen a lot of old questions about this, maybe now there are some solutions.
I want to take a screenshot of the current frame of my videoview. Videoview shows a real-time video using a rtsp-stream.
I try to take bitmap, but it's always black
public static Bitmap loadBitmapFromView(View v) {
Bitmap b = Bitmap.createBitmap(v.getLayoutParams().width , v.getLayoutParams().height, Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(b);
v.layout(0, 0, v.getLayoutParams().width, v.getLayoutParams().height);
v.draw(c);
return b;
}
EDIT :
MediaMetadataRetriever does not work with stream url, maybe works with video-file.
Using lib at this link (it's a wrapper of MediaMetadataRetriever that enable rtsp protocol input) I can save a frame of video, but there is a delay of 10 secs respect real-time videoview because it must create a new connection with streaming server.
I not test ThumbnailUtils, but in Api I read that input is only file-path
Use TextureView instead VideoView. TextureView has getBitmap() method. Here is the usage of TextureView as videoView
public class TextureVideoActivity extends Activity implements TextureView.SurfaceTextureListener {
private static final String FILE_NAME = "myVideo.mp4";
private static final String TAG = TextureVideoActivity.class.getName();
private MediaPlayer mMediaPlayer;
private TextureView mPreview;
#Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.activity_texture_video);
mPreview = (TextureView) findViewById(R.id.textureView);
mPreview.setSurfaceTextureListener(this);
}
public Bitmap getBitmap(){
return mPreview.getBitmap();
}
public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int width, int height) {
Surface surface = new Surface(surfaceTexture);
try {
mMediaPlayer = new MediaPlayer();
mMediaPlayer
.setDataSource(this, Uri.parse(FILE_NAME));
mMediaPlayer.setSurface(surface);
mMediaPlayer.setLooping(true);
// don't forget to call MediaPlayer.prepareAsync() method when you use constructor for
// creating MediaPlayer
mMediaPlayer.prepareAsync();
// Play video when the media source is ready for playback.
mMediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mediaPlayer) {
mediaPlayer.start();
}
});
} catch (IllegalArgumentException e) {
Log.d(TAG, e.getMessage());
} catch (SecurityException e) {
Log.d(TAG, e.getMessage());
} catch (IllegalStateException e) {
Log.d(TAG, e.getMessage());
} catch (IOException e) {
Log.d(TAG, e.getMessage());
}
}
#Override
protected void onDestroy() {
super.onDestroy();
if (mMediaPlayer != null) {
// Make sure we stop video and release resources when activity is destroyed.
mMediaPlayer.stop();
mMediaPlayer.release();
mMediaPlayer = null;
}
}
#Override
public void onSurfaceTextureSizeChanged(SurfaceTexture surfaceTexture, int i, int i2) {
}
#Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture) {
return false;
}
#Override
public void onSurfaceTextureUpdated(SurfaceTexture surfaceTexture) {
}
}
Playing video on TextureView