code: /*
   *
   *     Copyright (C) 2015 Ingo Fuchs
   *     This program is free software; you can redistribute it and/or modify
   *     it under the terms of the GNU General Public License as published by
   *     the Free Software Foundation; either version 2 of the License, or
   *     (at your option) any later version.
   *
   *     This program is distributed in the hope that it will be useful,
   *     but WITHOUT ANY WARRANTY; without even the implied warranty of
   *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   *     GNU General Public License for more details.
   *
   *     You should have received a copy of the GNU General Public License along
   *     with this program; if not, write to the Free Software Foundation, Inc.,
   *     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
   * /
   */
  
  package freed.cam.apis.camera1.modules;
  
  import android.media.MediaRecorder;
  import android.os.Build.VERSION;
  import android.os.Build.VERSION_CODES;
  import android.os.Handler;
  import android.os.ParcelFileDescriptor;
  import android.view.Surface;
  
  import com.troop.freedcam.R;
  
  import java.io.File;
  import java.io.IOException;
  import java.util.ArrayList;
  import java.util.List;
  
  import freed.ActivityAbstract;
  import freed.FreedApplication;
  import freed.cam.ActivityFreeDcamMain;
  import freed.cam.apis.basecamera.Size;
  import freed.cam.apis.basecamera.modules.ModuleAbstract;
  import freed.cam.apis.basecamera.record.VideoRecorder;
  import freed.cam.apis.camera1.Camera1;
  import freed.cam.apis.camera1.Camera1Utils;
  import freed.cam.apis.camera1.CameraHolder;
  import freed.cam.event.capture.CaptureStates;
  import freed.cam.previewpostprocessing.PreviewController;
  import freed.cam.ui.themesample.handler.UserMessageHandler;
  import freed.file.holder.FileHolder;
  import freed.settings.SettingKeys;
  import freed.utils.Log;
  import freed.utils.PermissionManager;
  
  /**
   * Created by troop on 06.01.2016.
   */
  public abstract class AbstractVideoModule extends ModuleAbstract<Camera1> implements MediaRecorder.OnInfoListener
  {
      VideoRecorder recorder;
      private String mediaSavePath;
      private final String TAG = AbstractVideoModule.class.getSimpleName();
      private ParcelFileDescriptor fileDescriptor;
      private final PermissionManager permissionManager;
      private final PreviewController preview;
      private final UserMessageHandler userMessageHandler;
  
      AbstractVideoModule(Camera1 cameraUiWrapper, Handler mBackgroundHandler, Handler mainHandler) {
          super(cameraUiWrapper,mBackgroundHandler,mainHandler);
          name = FreedApplication.getStringFromRessources(R.string.module_video);
          permissionManager = ActivityAbstract.permissionManager();
          preview = ActivityFreeDcamMain.previewController();
          userMessageHandler = ActivityFreeDcamMain.userMessageHandler();
      }
  
      @Override
      public void InitModule() {
          super.InitModule();
          changeCaptureState(CaptureStates.video_recording_stop);
          createPreview();
          initRecorder();
      }
  
  
      @Override
      public String ShortName() {
          return "Mov";
      }
  
      @Override
      public String LongName() {
          return "Movie";
      }
  
      //ModuleInterface START
      @Override
      public String ModuleName() {
          return name;
      }
  
      @Override
      public void DoWork()
      {
          startStopRecording();
      }
  
      private void startStopRecording()
      {
          if (!isWorking && !isLowStorage) {
              startRecording();
          }
          else if( isWorking ) {
              stopRecording();
          }
          if( isLowStorage ) {
              userMessageHandler.sendMSG("Can't Record due to low storage space. Free some and try again.", false);
          }
      }
  
      @Override
      public boolean IsWorking() {
          return isWorking;
      }
  
      @Override
      public void IsLowStorage(Boolean x) {
          isLowStorage = x;
      }
  //ModuleInterface END
  
      private void createPreview()
      {
          Size sizefromCam = new Size("1920x1080");
  
          List<Size> sizes = new ArrayList<>();
          String[] stringsSizes = cameraUiWrapper.getParameterHandler().get(SettingKeys.PREVIEW_SIZE).getStringValues();
          for (String s : stringsSizes) {
              sizes.add(new Size(s));
          }
          final Size size = Camera1Utils.getOptimalPreviewSize(sizes, sizefromCam.width, sizefromCam.height,false);
  
          if(size == null || preview.getSurfaceTexture() == null)
              return;
          cameraUiWrapper.getCameraHolder().StopPreview();
          preview.stop();
  
          preview.setSize(size.width, size.height);
          mainHandler.post(()->preview.setRotation(size.width, size.height, 0));
          if (cameraUiWrapper.getCameraHolder().canSetSurfaceDirect()) {
              cameraUiWrapper. getCameraHolder().setSurface((Surface)null);
              Surface surface = new Surface(preview.getSurfaceTexture());
              cameraUiWrapper.getCameraHolder().setSurface(surface);
          }
          else
              cameraUiWrapper.getCameraHolder().setTextureView(preview.getSurfaceTexture());
  
          Log.d(TAG, "set size to " + size.width + "x" + size.height);
          cameraUiWrapper.getParameterHandler().get(SettingKeys.PREVIEW_SIZE).setStringValue(size.width + "x" + size.height, false);
          cameraUiWrapper.getCameraHolder().fireOnCameraChangedAspectRatioEvent(size);
          cameraUiWrapper.getCameraHolder().StartPreview();
      }
  
      private void startRecording()
      {
          if (permissionManager.isPermissionGranted(PermissionManager.Permissions.RecordAudio)) {
              if (settingsManager.getGlobal(SettingKeys.LOCATION_MODE).get())
                  cameraUiWrapper.getCameraHolder().SetLocation(locationManager.getCurrentLocation());
              prepareRecorder();
          }
          else
              permissionManager.requestPermission(PermissionManager.Permissions.RecordAudio);
  
      }
  
      private void prepareRecorder()
      {
          try
          {
              Log.d(TAG, "InitMediaRecorder");
              isWorking = true;
              ((CameraHolder) cameraUiWrapper.getCameraHolder()).GetCamera().unlock();
              mediaSavePath = fileListController.getNewFilePath(settingsManager.GetWriteExternal(), ".mp4");
              File tosave = new File(mediaSavePath);
              recorder.setRecordingFile(tosave);
              recorder.setErrorListener((mr, what, extra) -> {
                  Log.e("MediaRecorder", "ErrorCode: " + what + " Extra: " + extra);
                  changeCaptureState(CaptureStates.video_recording_stop);
                  ((CameraHolder) cameraUiWrapper.getCameraHolder()).GetCamera().lock();
              });
  
  
              if (!tosave.getParentFile().exists())
                  tosave.getParentFile().mkdirs();
  
              recorder.setInfoListener(this);
  
              if (!settingsManager.get(SettingKeys.ORIENTATION_HACK).get().equals("0"))
                  recorder.setOrientation(Integer.parseInt(settingsManager.get(SettingKeys.ORIENTATION_HACK).get()));
              else
                  recorder.setOrientation(0);
  
              recorder.setPreviewSurface(((CameraHolder) cameraUiWrapper.getCameraHolder()).getSurfaceHolder());
  
  
                  Log.d(TAG,"Preparing Recorder");
                  if(recorder.prepare()) {
                      Log.d(TAG, "Recorder Prepared, Starting Recording");
                      recorder.start();
  
                  //fix exposer flick after video recording on first set parameters to camera.
                  // first call will under expose then second call will fix exposure.
                  cameraUiWrapper.getParameterHandler().SetParameters();
                  cameraUiWrapper.getParameterHandler().SetParameters();
  
                      Log.d(TAG, "Recording started");
                      sendStartToUi();
                  }
                  else
                  {
                      Log.e(TAG,"Recording failed");
                      userMessageHandler.sendMSG("Start Recording failed ",false);
                      isWorking = false;
                      ((CameraHolder) cameraUiWrapper.getCameraHolder()).GetCamera().lock();
                      recorder.reset();
                      isWorking = false;
                      sendStopToUi();
                  }
          }
          catch (NullPointerException ex)
          {
              Log.WriteEx(ex);
              userMessageHandler.sendMSG("Start Recording failed",false);
              isWorking = false;
              ((CameraHolder) cameraUiWrapper.getCameraHolder()).GetCamera().lock();
              recorder.reset();
              isWorking = false;
              sendStopToUi();
  
          }
      }
  
      private void sendStopToUi()
      {
          changeCaptureState(CaptureStates.video_recording_stop);
      }
  
      private void sendStartToUi()
      {
          changeCaptureState(CaptureStates.video_recording_start);
      }
  
      protected abstract void initRecorder();
  
      void stopRecording()
      {
          try {
              recorder.stop();
              Log.e(TAG, "Stop Recording");
          }
          catch (Exception ex)
          {
              Log.e(TAG, "Stop Recording failed, was called bevor start");
              userMessageHandler.sendMSG("Stop Recording failed, was called bevor start",false);
              Log.e(TAG,ex.getMessage());
              isWorking = false;
          }
          finally
          {
              cameraUiWrapper.getCameraHolder().GetCamera().lock();
              recorder.reset();
              isWorking = false;
              try {
                  if (VERSION.SDK_INT > VERSION_CODES.KITKAT && fileDescriptor != null) {
                      fileDescriptor.close();
                      fileDescriptor = null;
                  }
              } catch (IOException e1) {
                  Log.WriteEx(e1);
              }
              File file = new File(mediaSavePath);
              fireOnWorkFinish(new FileHolder(file,settingsManager.GetWriteExternal()));
              sendStopToUi();
          }
      }
  
      @Override
      public void onInfo(MediaRecorder mr, int what, int extra) {
          if (what == MediaRecorder.MEDIA_RECORDER_INFO_MAX_DURATION_REACHED)
          {
              recordnextFile(mr);
          }
          else if (what == MediaRecorder.MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED)
          {
              recordnextFile(mr);
          }
      }
  
      private void recordnextFile(MediaRecorder mr) {
  
          stopRecording();
          startRecording();
      }
  }
  