2

Serviceクラスを使用し、Camera APIを使用して背景ビデオレコーダーを作成しようとしています。私はアプリを実行すると、すべて正常に動作しているようだ。しかし、私がサービスを停止すると、私のアプリはANRをスローします。私はボタンの記録を停止押した後DDMS toolを使用して、私は私のmainスレッドに関する以下の情報が見つかりました:メディアレコーダーonStopのANR:ビデオレコーディングカメラ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) 

ServiceクラスのonDestroy()メソッドのコードを次のとおりです。

@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:整流Serviceクラス

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) { 

    } 
} 

誰かがなぜ私がANRを取得しているのかを理解するのを助けることができますonStop() Mediarecoderの方法? DDMSツールは、mainスレッドについて何を示していますか?

答えて

3

ライフサイクルコールバックServiceは、常にアプリケーションのメインスレッドで呼び出されます。 Serviceは、というユーザーの認識の観点からのみ、「バックグラウンド」で実行されます。真のバックグラウンド操作が必要な場合は、独自のスレッドを使用する必要があります。 MediaRecorder.stop()への呼び出しは、メディアサーバープロセスと通信して操作を停止するように指示するため、ブロッキングコールです。これは時間がかかりすぎています(他のメインスレッドのopsはブロックされて待機しています)、システムはあなたのアプリケーションのANRを宣言し、それを殺します。

+0

返信してくれてありがとう!私は 'SurfaceView'をビデオ録画に使用しています。あなたは 'Mediarecorder.onStop()'をどこに呼び出さなければならないか教えていただけますか?録音はボタンを押すことで停止します。 @Larry Schiefer –

+0

メディアレコーダーのすべてのやり取りを行う別のスレッドを実行させることができます。そのスレッドに停止するコマンドを送信するだけです。 –

+0

私のコードであなたの提案を実装しています。私のアプリはANRを投げていません。ディレクトリに正常に出力ファイルを作成しています。ただし、出力ファイルを再生すると、「このビデオを再生できません」というエラーが表示されます。私は完全な 'Service'クラスコードを投稿しました。物事がうまくいかないところを教えてください。 @Larry Schiefer –

関連する問題