[英]Android - Recording Video in the Background
我正在嘗試在Android中開發具有以下功能的應用程序:無縫錄制視頻和音頻,即使用戶前台有其他應用程序也是如此。 常見的情況是:用戶打開應用程序,開始錄制,然后打開導航應用程序或接聽電話。 我希望我的應用程序繼續記錄 。
我整理了一些代碼,主要是受本教程的啟發,下面將對此進行引用。 但是,我遇到了兩個問題:
1.當我按“主頁”鍵時,錄像凍結,但是聲音很好
2.當我導航回該應用程序時,預覽為黑色
我的問題是:
我的代碼:
public class GlobalState extends Application { private boolean recording = false; private boolean loggingEnabled = true; private Camera serviceCamera = null; private CameraPreview cameraPreview = null; @Override public void onCreate() { try { serviceCamera = Camera.open(); } catch (Exception e) { } super.onCreate(); } public boolean isRecording() { return recording; } public boolean isLoggingEnabled() { return loggingEnabled; } public void setRecording(boolean recording) { this.recording = recording; } public void setCamera(Camera serviceCamera) { this.serviceCamera = serviceCamera; } public Camera getCamera() { return serviceCamera; } public void setCameraPreview(CameraPreview cameraPreview) { this.cameraPreview = cameraPreview; } public CameraPreview getCameraPreview() { return this.cameraPreview; } }
public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback { private SurfaceHolder mHolder; private Camera mCamera; private static final String TAG = "CameraPreview"; public CameraPreview(Context context, Camera camera) { super(context); mCamera = camera; mHolder = getHolder(); mHolder.addCallback(this); mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); } public void surfaceCreated(SurfaceHolder holder) { try { mCamera.setPreviewDisplay(holder); mCamera.startPreview(); } catch (IOException e) { Log.d(TAG, "Error setting camera preview: " + e.getMessage()); } } public void surfaceDestroyed(SurfaceHolder holder) { } public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) { if (mHolder.getSurface() == null) { return; } try { mCamera.stopPreview(); } catch (Exception e) { } try { mCamera.setPreviewDisplay(mHolder); mCamera.startPreview(); } catch (Exception e) { Log.d(TAG, "Error starting camera preview: " + e.getMessage()); } } }
public class MainActivity extends Activity { public String TAG = "DE-MainActivity"; ImageView mRecordView; ImageView mMenuButtonView; LinearLayout mMenuView; TextView mVideosTextView; TextView mSettingsTextView; private Camera mCamera; private CameraPreview mPreview; GlobalState mAppState = null; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mAppState = (GlobalState) getApplicationContext(); if (mAppState.isLoggingEnabled()) { Log.v(TAG, "Activity: onCreate"); } mCamera = mAppState.getCamera(); if (mAppState.getCameraPreview() == null) { mPreview = new CameraPreview(this, mCamera); mAppState.setCameraPreview(mPreview); } FrameLayout preview = (FrameLayout) findViewById(R.id.fl_camera); preview.addView(mPreview); mMenuView = (LinearLayout) findViewById(R.id.ll_menu_list); mVideosTextView = (TextView) findViewById(R.id.tv_menu_item_videos); mSettingsTextView = (TextView) findViewById(R.id.tv_menu_item_settings); mRecordView = (ImageView) findViewById(R.id.iv_record); mRecordView.setImageResource(R.drawable.btn_not_recording); mRecordView.setAlpha((float) 0.5); mRecordView.bringToFront(); mRecordView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (!mAppState.isRecording()) { mRecordView.setImageResource(R.drawable.btn_recording); mRecordView.setAlpha((float) 0.3); startService(new Intent(MainActivity.this, RecorderService.class)); } else { mRecordView.setImageResource(R.drawable.btn_not_recording); mRecordView.setAlpha((float) 0.5); stopService(new Intent(MainActivity.this, RecorderService.class)); } } }); mMenuButtonView = (ImageView) findViewById(R.id.iv_menu); mMenuButtonView.setImageResource(R.drawable.btn_menu); mMenuButtonView.setAlpha((float) 0.5); mMenuButtonView.bringToFront(); mMenuButtonView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (mMenuView.getVisibility() == View.VISIBLE) { mMenuView.setVisibility(View.INVISIBLE); } else { mMenuView.setVisibility(View.VISIBLE); } } }); mSettingsTextView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (mAppState.isLoggingEnabled()) Log.v(TAG, "settings clicked!"); } }); mVideosTextView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (mAppState.isLoggingEnabled()) Log.v(TAG, "videos clicked!"); } }); } @Override protected void onDestroy() { if (mAppState.isLoggingEnabled()) Log.v(TAG, "APPLICATION EXIT!"); if (mCamera != null) { mCamera.release(); // release the camera for other applications mCamera = null; } super.onDestroy(); } public boolean onCreateOptionsMenu(Menu menu) { if (mMenuView.getVisibility() == View.VISIBLE) { mMenuView.setVisibility(View.INVISIBLE); } else { mMenuView.setVisibility(View.VISIBLE); } return false; } }
public class RecorderService extends Service { private static final String TAG = "RecorderService"; private static Camera mServiceCamera; private MediaRecorder mMediaRecorder; private GlobalState mAppState; public static final int MEDIA_TYPE_IMAGE = 1; public static final int MEDIA_TYPE_VIDEO = 2; @Override public void onCreate() { mAppState = (GlobalState) getApplicationContext(); mServiceCamera = mAppState.getCamera(); if (mAppState.isLoggingEnabled()) Log.v(TAG, "onCreate"); } @Override public int onStartCommand(Intent intent, int flags, int startId) { super.onStartCommand(intent, flags, startId); if (!mAppState.isRecording()) { if (prepareVideoRecorder()) { mMediaRecorder.start(); mAppState.setRecording(true); } else { releaseMediaRecorder(); } } return 5; } @Override public IBinder onBind(Intent intent) { return null; } @Override public void onDestroy() { if (mAppState.isLoggingEnabled()) Log.v(TAG, "onDestroy"); // stop recording and release camera mMediaRecorder.stop(); // stop the recording releaseMediaRecorder(); // release the MediaRecorder object mServiceCamera.lock(); // take camera access back from MediaRecorder mAppState.setRecording(false); super.onDestroy(); } private void releaseMediaRecorder() { if (mMediaRecorder != null) { mMediaRecorder.reset(); // clear recorder configuration mMediaRecorder.release(); // release the recorder object mMediaRecorder = null; } } /** Create a file Uri for saving an image or video */ private static Uri getOutputMediaFileUri(int type) { return Uri.fromFile(getOutputMediaFile(type)); } /** Create a File for saving an image or video */ private static File getOutputMediaFile(int type) { File mediaStorageDir = new File( Environment.getExternalStorageDirectory(), "DashEyeApp"); if (!mediaStorageDir.exists()) { if (!mediaStorageDir.mkdirs()) { Log.d("MyCameraApp", "failed to create directory"); return null; } } String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss") .format(new Date()); File mediaFile; if (type == MEDIA_TYPE_IMAGE) { mediaFile = new File(mediaStorageDir.getPath() + File.separator + "IMG_" + timeStamp + ".jpg"); } else if (type == MEDIA_TYPE_VIDEO) { mediaFile = new File(mediaStorageDir.getPath() + File.separator + "VID_" + timeStamp + ".mp4"); } else { return null; } return mediaFile; } private boolean prepareVideoRecorder() { mMediaRecorder = new MediaRecorder(); mServiceCamera.unlock(); mMediaRecorder.setCamera(mServiceCamera); mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER); mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA); mMediaRecorder.setProfile(CamcorderProfile .get(CamcorderProfile.QUALITY_HIGH)); mMediaRecorder.setOutputFile(getOutputMediaFile(MEDIA_TYPE_VIDEO) .toString()); // Step 5: Set the preview output // mMediaRecorder.setPreviewDisplay(mAppState.getCameraPreview().getHolder().getSurface()); try { mMediaRecorder.prepare(); } catch (IllegalStateException e) { Log.d(TAG, "IllegalStateException preparing MediaRecorder: " + e.getMessage()); releaseMediaRecorder(); return false; } catch (IOException e) { Log.d(TAG, "IOException preparing MediaRecorder: " + e.getMessage()); releaseMediaRecorder(); return false; } return true; } }
我的解決方案很好,請嘗試:
服務Android:
import java.util.Calendar;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.graphics.PixelFormat;
import android.hardware.Camera;
import android.hardware.Camera.CameraInfo;
import android.media.CamcorderProfile;
import android.media.MediaRecorder;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.view.Gravity;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.WindowManager;
import android.view.WindowManager.LayoutParams;
public class BackgroundVideoRecorder extends Service implements
SurfaceHolder.Callback {
private WindowManager windowManager;
private SurfaceView surfaceView;
private Camera camera = null;
private MediaRecorder mediaRecorder = null;
int contTime = 0, duracaoGravacao = 30; //interval in seconds to record video
private class thread implements Runnable {
public void run() {
contTime++;
if (contTime >= duracaoGravacao) {
StopService();
}
tick_Handler.postDelayed(tick_thread, 1000);
}
}
Handler tick_Handler;
thread tick_thread;
Preferences pref;
@Override
public void onCreate() {
windowManager = (WindowManager) this
.getSystemService(Context.WINDOW_SERVICE);
surfaceView = new SurfaceView(this);
LayoutParams layoutParams = new WindowManager.LayoutParams(1, 1,
WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY,
WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH,
PixelFormat.TRANSLUCENT);
layoutParams.gravity = Gravity.LEFT | Gravity.TOP;
windowManager.addView(surfaceView, layoutParams);
surfaceView.getHolder().addCallback(this);
tick_Handler = new Handler();
tick_thread = new thread();
VIDEO_RECORDER_FOLDER = new _Path().getPathVideo();
}
@Override
public void onStart(Intent intent, int startId) {
tick_Handler.post(tick_thread);
}
// Method called right after Surface created (initializing and starting
// MediaRecorder)
@Override
public void surfaceCreated(SurfaceHolder surfaceHolder) {
boolean found = false;
int i = 0;
try {
for (i = 0; i < Camera.getNumberOfCameras(); i++) {
Camera.CameraInfo newInfo = new Camera.CameraInfo();
Camera.getCameraInfo(i, newInfo);
if (newInfo.facing == CameraInfo.CAMERA_FACING_FRONT) {
found = true;
break;
}
}
} catch (Exception e) {
e.printStackTrace();
}
if (found) {
camera = Camera.open(i);
} else {
camera = Camera.open();
}
Calendar lCDateTime = Calendar.getInstance();
String t = String.valueOf(lCDateTime.getTimeInMillis());
nomeArquivo = "hire_me_now_" + t + ".mp4";
nomeArquivo = nomeArquivo.replace(" ", "_").replace(":", "_")
.replace("-", "_");
String caminhoArquivo = VIDEO_RECORDER_FOLDER + "/" + nomeArquivo;
mediaRecorder = new MediaRecorder();
camera.unlock();
mediaRecorder.setPreviewDisplay(surfaceHolder.getSurface());
mediaRecorder.setCamera(camera);
mediaRecorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER);
mediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
mediaRecorder.setProfile(CamcorderProfile
.get(CamcorderProfile.QUALITY_QVGA));
mediaRecorder.setVideoFrameRate(15);
mediaRecorder.setOutputFile(caminhoArquivo);
try {
mediaRecorder.prepare();
} catch (Exception e) {
e.printStackTrace();
}
mediaRecorder.start();
}
// Stop recording and remove SurfaceView
@Override
public void onDestroy() {
mediaRecorder.stop();
mediaRecorder.reset();
mediaRecorder.release();
camera.lock();
camera.release();
windowManager.removeView(surfaceView);
}
protected void StopService() {
try {
this.stopSelf();
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void surfaceChanged(SurfaceHolder surfaceHolder, int format,
int width, int height) {
}
@Override
public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
}
事實證明,Android不喜歡預覽被破壞 (例如,當用戶單擊“主頁”按鈕時),因此它會切斷視頻錄制。
解決方法是使用WindowManager設置覆蓋,並在用戶單擊“主頁”按鈕時將其調整為1x1。 我在這里找到了解決方案。 非常感謝cman !
它可以完成,但是從API級別23開始,您需要詢問相機權限,您可以參考以下答案https://stackoverflow.com/a/49919386/4604234
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.