Ich habe versucht, einen Hintergrundvideo-Rekorder zu erstellen, der Service
Klasse verwendet und Camera API
verwendet. Wenn ich die App starte, scheint alles in Ordnung zu sein. Wenn ich jedoch den Dienst stoppe, löst meine App ANR aus. Mit DDMS tool
, fand ich folgende Informationen über meinen main
Faden, nachdem ich Stoppen der Aufnahme-Taste drücken:ANR auf Mediarekorder onStop: Videoaufzeichnungskamera API
at android.media.MediaRecorder.stop(Native Method)
at com.svtech.thirdeye.thirdeye.Services.VideoRecordingOldApiService.stopRecording(VideoRecordingOldApiService.java:212)
at com.svtech.thirdeye.thirdeye.Services.VideoRecordingOldApiService.onDestroy(VideoRecordingOldApiService.java:250)
at android.app.ActivityThread.handleStopService(ActivityThread.java:2877)
at android.app.ActivityThread.access$2000(ActivityThread.java:162)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1466)
at android.os.Handler.dispatchMessage(Handler.java:107)
at android.os.Looper.loop(Looper.java:194)
at android.app.ActivityThread.main(ActivityThread.java:5371)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:525)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:833)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:600)
at dalvik.system.NativeStart.main(Native Method)
Der onDestroy()
Methodencode meiner Service
Klasse wird wie folgt:
@Override
public void onDestroy() {
super.onDestroy();
if (linearLayout != null) {
windowManager.removeView(linearLayout);
linearLayout = null;
}
mMediaRecorder.stop();
if (mMediaRecorder != null) {
mMediaRecorder.reset();
mMediaRecorder.release();
mMediaRecorder = null;
}
if (mCamera != null) {
mCamera.release();
mCamera.lock();
mCamera = null;
}
}
EDITS: Rektifiziertes Service
Klasse
public class VideoRecordingOldApiService extends Service implements SurfaceHolder.Callback {
private Camera mCamera;
private String outputFile;
private MediaRecorder mMediaRecorder;
private CameraCheck cameraCheck;
private final static String TAG = "VideoRecorderService";
private LinearLayout linearLayout;
private SurfaceView surfaceView;
private SurfaceHolder surfaceHolder;
private WindowManager windowManager;
private Thread thread;
private Handler handler;
public VideoRecordingOldApiService() {
}
@Override
public void onCreate() {
linearLayout = (LinearLayout) LayoutInflater.from(this).inflate(R.layout.surfaceview_layout, null);
linearLayout.setLayoutParams(new LinearLayout.LayoutParams(
getResources().getDimensionPixelSize(R.dimen.textureView_width), getResources().getDimensionPixelSize(R.dimen.textureView_height)));
windowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams(
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.TYPE_SYSTEM_ALERT,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
PixelFormat.OPAQUE
);
layoutParams.gravity = Gravity.TOP | Gravity.LEFT;
windowManager.addView(linearLayout, layoutParams);
surfaceView = (SurfaceView) linearLayout.findViewById(R.id.videoSurfaceView);
surfaceHolder = surfaceView.getHolder();
surfaceHolder.addCallback(this);
surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
cameraCheck = new CameraCheck();
handler = new Handler();
//Setup Notification
final Intent notificationIntent = new Intent(getApplicationContext(), MainActivity.class);
notificationIntent.addCategory("android.intent.category.LAUNCHER");
final PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, notificationIntent, 0);
Notification notification;
notification = new Notification.Builder(getApplicationContext())
.setSmallIcon(R.drawable.videorecordicon)
.setOngoing(true)
.setPriority(Notification.PRIORITY_DEFAULT)
.setContentTitle(getResources().getString(R.string.video_recorder_notification))
.setContentText(getResources().getString(R.string.notification_video_text) + "...")
.setContentIntent(pendingIntent).build();
startForeground(1, notification);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (initCamera()) {
thread = new Thread(new Runnable() {
@Override
public void run() {
Looper.prepare();
if (initRecorder()) {
mMediaRecorder.start();
handler.post(new Runnable() {
@Override
public void run() {
Toast.makeText(getApplicationContext(), R.string.video_recorder_started, Toast.LENGTH_LONG).show();
}
});
} else {
handler.post(new Runnable() {
@Override
public void run() {
Toast.makeText(getApplicationContext(), "Couldn't start MediaRecorder", Toast.LENGTH_LONG).show();
}
});
}
}
});
thread.start();
} else {
Toast.makeText(getApplicationContext(), "Camera not found", Toast.LENGTH_SHORT).show();
}
return START_REDELIVER_INTENT;
}
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
throw new UnsupportedOperationException("Not yet implemented");
}
public String getFileName() {
String fileName = null;
if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
fileName = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES)
+ "/" + "video_record" + System.currentTimeMillis() + ".mp4";
Log.i("Camera Recorder", fileName);
} else {
Toast.makeText(getApplicationContext(), "No External Storage Found", Toast.LENGTH_LONG).show();
}
return fileName;
}
private boolean initCamera() {
try {
mCamera = cameraCheck.getDesiredCamera(this, Camera.CameraInfo.CAMERA_FACING_BACK);
Camera.Parameters parameters = mCamera.getParameters();
parameters.setRecordingHint(true);
Camera.Size previewSize = getOptimalPreviewSize(parameters.getSupportedPreviewSizes(),
surfaceView.getWidth(), surfaceView.getHeight());
parameters.setPreviewSize(previewSize.width, previewSize.height);
mCamera.setDisplayOrientation(90);
} catch (Exception e) {
Log.v(TAG, "Could not initialise the camera");
e.printStackTrace();
return false;
}
return true;
}
private boolean initRecorder() {
mMediaRecorder = new MediaRecorder();
outputFile = getFileName();
CamcorderProfile profile = CamcorderProfile.get(Camera.CameraInfo.CAMERA_FACING_BACK, CamcorderProfile.QUALITY_HIGH);
profile.videoFrameWidth = mCamera.getParameters().getSupportedVideoSizes().get(1).width;
profile.videoFrameHeight = mCamera.getParameters().getSupportedVideoSizes().get(1).height;
profile.fileFormat = MediaRecorder.OutputFormat.MPEG_4;
profile.audioCodec = MediaRecorder.AudioEncoder.AAC;
profile.videoCodec = MediaRecorder.VideoEncoder.H264;
try {
mCamera.unlock();
mMediaRecorder.setCamera(mCamera);
mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.CAMCORDER);
mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
mMediaRecorder.setProfile(profile);
mMediaRecorder.setOutputFile(outputFile);
//mMediaRecorder.setPreviewDisplay(surfaceHolder.getSurface());
mMediaRecorder.prepare();
Log.v(TAG, "MediaRecorder initialized successfully");
} catch (Exception e) {
Log.v(TAG, "MediaRecorder failed to initialize");
e.printStackTrace();
return false;
}
return true;
}
private void releaseCamera() {
if (mCamera != null) {
mCamera.lock();
mCamera.release();
mCamera = null;
}
}
@Override
public void onDestroy() {
super.onDestroy();
if (!thread.isInterrupted()) {
thread.interrupt();
Toast.makeText(getApplicationContext(), R.string.recording_done, Toast.LENGTH_LONG).show();
}
if (linearLayout != null) {
windowManager.removeView(linearLayout);
linearLayout = null;
}
stopForeground(true);
releaseCamera();
thread = null;
}
private Camera.Size getOptimalPreviewSize(List<Camera.Size> sizes, int w, int h) {
final double ASPECT_TOLERANCE = 0.1;
double targetRatio = (double) h/w;
if (sizes == null)
return null;
Camera.Size optimalSize = null;
double minDiff = Double.MAX_VALUE;
int targetHeight = h;
for (Camera.Size size : sizes) {
double ratio = (double) size.height/size.width;
if (Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE)
continue;
if (Math.abs(size.height - targetHeight) < minDiff) {
optimalSize = size;
minDiff = Math.abs(size.height - targetHeight);
}
}
if (optimalSize == null) {
minDiff = Double.MAX_VALUE;
for (Camera.Size size : sizes) {
if (Math.abs(size.height - targetHeight) < minDiff) {
optimalSize = size;
minDiff = Math.abs(size.height - targetHeight);
}
}
}
return optimalSize;
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
try {
mCamera.setPreviewDisplay(holder);
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
}
}
Kann mir jemand helfen herauszufinden, warum ich ANR auf onStop()
Methode von Mediarecoder bekomme? Und was zeigt DDMS-Tool über main
Thread ???
Danke für die Antwort Mann! Ich verwende 'SurfaceView' für Videoaufnahmen. Können Sie mir sagen, wo soll ich 'Mediarecorder.onStop()' ?? Die Aufnahme soll durch Tastendruck gestoppt werden. @Larry Schiefer –
Sie können einen separaten Thread ausführen, der alle Interaktionen des Medienrecorders ausführt. Senden Sie diesem Thread einfach einen Befehl zum Stoppen. –
Ich habe Ihren Vorschlag in meinem Code umgesetzt .. Jetzt wirft meine App nicht ANR. Es erstellt erfolgreich eine Ausgabedatei im Verzeichnis. Wenn ich jedoch die Ausgabedatei abspiele, erhalte ich den Fehler "Kann dieses Video nicht abspielen". Ich habe meinen vollständigen 'Service'-Klassencode gepostet. Kannst du mir bitte sagen, wo die Dinge schief gehen ??? @Larry Schiefer –