공감 및 댓글, 광고클릭은 포스팅 하는데 아주아주 큰 힘이 됩니다!! 포스팅 내용이 찾아주신 분들께 도움이 되길 바라며 더 깔끔하고 좋은 포스팅을 만들어 나가겠습니다^^
|
이번 포스팅에서는 안드로이드의 Camera2 api를 사용해서 동영상을 촬영하는
방법에 대해 간단히 소개합니다.
대부분이 코드로 이루어져있기 때문에
코드만 잘 보시면 쉽게 적용하실 수 있을 거에요.
1. 앱 레벨 build.gradle에 dataBinding, java 1.8 추가
android{} 블록 안에
dataBinding {
enabled true
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
추가해 줍니다.
compileOptions 안에 설정은 람다식을 사용하기 위한 코드이기 때문에
람다식을 안쓰시면 추가안해도 됩니다.
의존성 추가
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation 'com.android.support:appcompat-v7:28.0.0'
implementation 'com.android.support:support-v13:28.0.0'
implementation 'com.android.support:design:28.0.0'
implementation 'com.android.support:support-v4:28.0.0'
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
implementation 'io.reactivex.rxjava2:rxjava:2.2.2'
implementation 'io.reactivex.rxjava2:rxandroid:2.1.0'
}
녹화시간 타이머에 RxJava를 사용해서 저는 아래 두 의존성을 추가했습니다.
2. AndroidManifest.xml 에 uses-permission, uses-feature 추가
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-feature android:name="android.hardware.camera2.full" />
<uses-feature android:name="android.hardware.location.gps" />
<uses-feature android:name="android.hardware.camera.autofocus" />
3. VideoActivity.java, activity_video.xml
import android.databinding.DataBindingUtil;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import superbrain.rowan.com.questionnaire.databinding.ActivityVideoBinding;
public class VideoActivity extends AppCompatActivity {
ActivityVideoBinding binding;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
binding = DataBindingUtil.setContentView(this, R.layout.activity_video);
if (null == savedInstanceState) {
getSupportFragmentManager().beginTransaction()
.replace(R.id.container, VideoFragment.newInstance())
.commit();
}
}
}
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<android.support.constraint.ConstraintLayout
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#000"
tools:context=".VideoActivity">
</android.support.constraint.ConstraintLayout>
</layout>
기본 틀이 되는 Activity와 xml은 별 코드가 없습니다.
실제 사용되는 건 Fragment입니다.
VideoActivity 클래스에서 getSupport....replace(R.id.container, VideoFragment.newIntance()) 코드가 보이죠?
id가 container인 activity_Video의 ConstraintLayout에 VideoFragment를 대입합니다.
4. fragment_video.xml ( 저는 가로, 세로 모두 디자인했습니다.)
가로
<?xml version="1.0" encoding="utf-8"?>
<layout>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<superbrain.rowan.com.questionnaire.VideoRecordingView
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="@+id/control"
android:id="@+id/preview"
android:layout_width="match_parent"
android:layout_height="0dp" />
<FrameLayout
android:id="@+id/control"
android:layout_width="match_parent"
android:layout_height="112dp"
android:background="@color/control_background"
app:layout_constraintBottom_toBottomOf="parent">
<Button
android:id="@+id/pictureBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="@string/record" />
<ImageButton
android:id="@+id/switchImgBtn"
style="@android:style/Widget.Material.Light.Button.Borderless"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical|right"
android:contentDescription="@string/description_info"
android:padding="20dp"
android:src="@drawable/ic_action_info" />
</FrameLayout>
<TextView
android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent"
android:padding="8dp"
android:drawableStart="@drawable/icon_record"
android:id="@+id/recordTimeTxtView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@android:color/black"
android:textSize="16sp"/>
</android.support.constraint.ConstraintLayout>
</layout>
세로
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<android.support.constraint.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<superbrain.rowan.com.questionnaire.VideoRecordingView
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@+id/control"
android:id="@+id/preview"
android:layout_width="0dp"
android:layout_height="match_parent"/>
<FrameLayout
android:id="@+id/control"
android:layout_width="wrap_content"
android:layout_height="match_parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:background="@color/control_background"
android:orientation="horizontal">
<Button
android:id="@+id/pictureBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="@string/record" />
<ImageButton
android:id="@+id/switchImgBtn"
style="@android:style/Widget.Material.Light.Button.Borderless"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal|bottom"
android:contentDescription="@string/description_info"
android:padding="20dp"
android:src="@drawable/ic_action_info" />
</FrameLayout>
</android.support.constraint.ConstraintLayout>
</layout>
5. 커스텀 TextureView
import android.content.Context;
import android.util.AttributeSet;
import android.view.TextureView;
public class VideoRecordingView extends TextureView {
int mRatioWidth = 0;
int mRatioHeight = 0;
public VideoRecordingView(Context context) {
this(context, null);
}
public VideoRecordingView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public VideoRecordingView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public void setAspectRatio(int width, int height) {
if (width < 0 || height < 0) {
throw new IllegalArgumentException("Size cannot be negative.");
}
mRatioWidth = width;
mRatioHeight = height;
requestLayout();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
if (0 == mRatioWidth || 0 == mRatioHeight) {
setMeasuredDimension(width, height);
} else {
if (width < height * mRatioWidth / mRatioHeight) {
setMeasuredDimension(width, width * mRatioHeight / mRatioWidth);
} else {
setMeasuredDimension(height * mRatioWidth / mRatioHeight, height);
}
}
}
}
[ 광고 보고 가시죠! ]
[ 감사합니다! ]
6. VideoFragment.java ( 코드가 좀 길어요 ㅎㅎ)
package superbrain.rowan.com.questionnaire;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.res.Configuration;
import android.databinding.DataBindingUtil;
import android.graphics.Matrix;
import android.graphics.RectF;
import android.graphics.SurfaceTexture;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraManager;
import android.hardware.camera2.CameraMetadata;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.params.StreamConfigurationMap;
import android.media.MediaRecorder;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.HandlerThread;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.util.Log;
import android.util.Size;
import android.util.SparseIntArray;
import android.view.LayoutInflater;
import android.view.Surface;
import android.view.TextureView;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import io.reactivex.Observable;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
import superbrain.rowan.com.questionnaire.databinding.FragmentVideoBinding;
public class VideoFragment extends Fragment implements View.OnClickListener {
private static final String TAG = "VideoFragment";
//Superbrain 대신 원하는 폴더이름을 만들면 됩니다.
private static final String DETAIL_PATH = "DCIM/Superbrain/";
FragmentVideoBinding binding;
// 카메라 광각, 전면, 후면
private static final String CAM_WHAT = "2";
private static final String CAM_FRONT = "1";
private static final String CAM_REAR = "0";
private String mCamId;
CameraCaptureSession mCameraCaptureSession;
CameraDevice mCameraDevice;
CameraManager mCameraManager;
Size mVideoSize;
Size mPreviewSize;
CaptureRequest.Builder mCaptureRequestBuilder;
int mSensorOrientation;
Semaphore mSemaphore = new Semaphore(1);
HandlerThread mBackgroundThread;
Handler mBackgroundHandler;
MediaRecorder mMediaRecorder;
private String mNextVideoAbsolutePath;
private boolean mIsRecordingVideo;
public static VideoFragment newInstance() {
return new VideoFragment();
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
binding = DataBindingUtil.inflate(inflater, R.layout.fragment_video, container, false);
mCamId = CAM_REAR;
mCompositeDisposable = new CompositeDisposable();
return binding.getRoot();
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
binding.pictureBtn.setOnClickListener(this);
binding.switchImgBtn.setOnClickListener(this);
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
assert getActivity() != null;
}
@Override
public void onResume() {
super.onResume();
startBackgroundThread();
if (binding.preview.isAvailable()) {
openCamera(binding.preview.getWidth(), binding.preview.getHeight());
} else {
binding.preview.setSurfaceTextureListener(mSurfaceTextureListener);
}
}
@Override
public void onPause() {
closeCamera();
stopBackgroundThread();
super.onPause();
}
private TextureView.SurfaceTextureListener mSurfaceTextureListener = new TextureView.SurfaceTextureListener() {
@Override
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
openCamera(binding.preview.getWidth(), binding.preview.getHeight());
}
@Override
public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
if(!mIsRecordingVideo) configureTransform(width, height);
}
@Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
return true;
}
@Override
public void onSurfaceTextureUpdated(SurfaceTexture surface) {
}
};
private CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() {
@Override
public void onOpened(@NonNull CameraDevice camera) {
mCameraDevice = camera;
startPreview();
mSemaphore.release();
if (null != binding.preview) {
configureTransform(binding.preview.getWidth(), binding.preview.getHeight());
}
}
@Override
public void onDisconnected(@NonNull CameraDevice camera) {
mSemaphore.release();
camera.close();
mCameraDevice = null;
}
@Override
public void onError(@NonNull CameraDevice camera, int error) {
mSemaphore.release();
camera.close();
mCameraDevice = null;
Activity activity = getActivity();
assert activity != null;
activity.finish();
}
};
//카메라 기능 호출
private void openCamera(int width, int height) {
Activity activity = getActivity();
assert activity != null;
mCameraManager = (CameraManager) activity.getSystemService(Context.CAMERA_SERVICE);
try {
if (!mSemaphore.tryAcquire(2500, TimeUnit.MILLISECONDS)) {
throw new RuntimeException("Time out waiting to lock camera opening.");
}
CameraCharacteristics cc = mCameraManager.getCameraCharacteristics(mCamId);
StreamConfigurationMap scm = cc.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);
mSensorOrientation = cc.get(CameraCharacteristics.SENSOR_ORIENTATION);
if (scm == null) {
throw new RuntimeException("Cannot get available preview/video sizes");
}
mVideoSize = chooseVideoSize(scm.getOutputSizes(MediaRecorder.class));
mPreviewSize = chooseOptimalSize(scm.getOutputSizes(SurfaceTexture.class), width, height, mVideoSize);
int orientation = getResources().getConfiguration().orientation;
if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
binding.preview.setAspectRatio(mPreviewSize.getWidth(), mPreviewSize.getHeight());
} else {
binding.preview.setAspectRatio(mPreviewSize.getHeight(), mPreviewSize.getWidth());
}
configureTransform(width, height);
mMediaRecorder = new MediaRecorder();
mCameraManager.openCamera(mCamId, mStateCallback, mBackgroundHandler);
} catch (CameraAccessException | SecurityException | NullPointerException | InterruptedException e) {
e.printStackTrace();
activity.finish();
}
}
private static Size chooseVideoSize(Size[] choices) {
for (Size size : choices) {
// 해상도에 맞게 설정하면 될듯?
if(size.getWidth() == size.getHeight() * 4 / 3 && size.getWidth() <= 1080){
return size;
}
}
return choices[choices.length - 1];
}
private static Size chooseOptimalSize(Size[] choices, int width, int height, Size aspectRatio) {
List<Size> bigEnough = new ArrayList<>();
int w = aspectRatio.getWidth();
int h = aspectRatio.getHeight();
for (Size ops : choices) {
if(ops.getHeight() == ops.getWidth() * h / w && ops.getWidth() >= width && ops.getHeight() >= height) {
bigEnough.add(ops);
}
}
if (bigEnough.size() > 0) {
return Collections.min(bigEnough, new CompareSizesByArea());
} else {
return choices[0];
}
}
// 카메라 닫기
private void closeCamera() {
try {
mSemaphore.acquire();
closePreviewSession();
if (null != mCameraDevice) {
mCameraDevice.close();
mCameraDevice = null;
}
if (null != mMediaRecorder) {
mMediaRecorder.release();
mMediaRecorder = null;
}
} catch (InterruptedException ie) {
ie.printStackTrace();
} finally {
mSemaphore.release();
}
}
//미리보기 기능
private void startPreview() {
if(null == mCameraDevice || !binding.preview.isAvailable() || null == mPreviewSize) {
return;
}
try {
closePreviewSession();
SurfaceTexture texture = binding.preview.getSurfaceTexture();
assert texture != null;
texture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());
mCaptureRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
Surface previewSurface = new Surface(texture);
mCaptureRequestBuilder.addTarget(previewSurface);
mCameraDevice.createCaptureSession(Collections.singletonList(previewSurface), new CameraCaptureSession.StateCallback() {
@Override
public void onConfigured(@NonNull CameraCaptureSession session) {
mCameraCaptureSession = session;
updatePreview();
}
@Override
public void onConfigureFailed(@NonNull CameraCaptureSession session) {
Activity activity = getActivity();
assert activity != null;
Toast.makeText(activity, "Failed", Toast.LENGTH_SHORT).show();
}
}, mBackgroundHandler);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
private void updatePreview() {
if (null == mCameraDevice) {
return;
}
try {
setUpCaptureRequestBuilder(mCaptureRequestBuilder);
HandlerThread thread = new HandlerThread("CameraPreview");
thread.start();
mCameraCaptureSession.setRepeatingRequest(mCaptureRequestBuilder.build(), null, mBackgroundHandler);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
private void setUpCaptureRequestBuilder(CaptureRequest.Builder builder) {
builder.set(CaptureRequest.CONTROL_MODE, CameraMetadata.CONTROL_MODE_AUTO);
}
private void configureTransform(int viewWidth, int viewHeight) {
Activity activity = getActivity();
if (null == binding.preview || null == mPreviewSize || null == activity) {
return;
}
int rotation = activity.getWindowManager().getDefaultDisplay().getRotation();
Matrix matrix = new Matrix();
RectF viewRect = new RectF(0, 0, viewWidth, viewHeight);
RectF bufferRect = new RectF(0, 0, mPreviewSize.getWidth(), mPreviewSize.getHeight());
float centerX = viewRect.centerX();
float centerY = viewRect.centerY();
if (Surface.ROTATION_90 == rotation || Surface.ROTATION_270 == rotation) {
bufferRect.offset(centerX - bufferRect.centerX(), centerY - bufferRect.centerY());
matrix.setRectToRect(viewRect, bufferRect, Matrix.ScaleToFit.FILL);
float scale = Math.max(
(float) viewHeight / mPreviewSize.getHeight(),
(float) viewWidth / mPreviewSize.getWidth()
);
matrix.postScale(scale, scale, centerX, centerY);
matrix.postRotate(90 * (rotation - 2), centerX, centerY);
}
activity.runOnUiThread(() -> binding.preview.setTransform(matrix));
}
private void startBackgroundThread() {
mBackgroundThread = new HandlerThread("CameraBackground");
mBackgroundThread.start();
mBackgroundHandler = new Handler(mBackgroundThread.getLooper());
}
private void stopBackgroundThread() {
mBackgroundThread.quitSafely();
try {
mBackgroundThread.join();
mBackgroundThread = null;
mBackgroundHandler = null;
} catch (Exception e) {
e.printStackTrace();
}
}
private void closePreviewSession() {
if (mCameraCaptureSession != null) {
mCameraCaptureSession.close();
mCameraCaptureSession = null;
}
}
private static final int SENSOR_ORIENTATION_DEFAULT_DEGREES = 90;
private static final int SENSOR_ORIENTATION_INVERSE_DEGREES = 270;
private static final SparseIntArray DEFAULT_ORIENTATIONS = new SparseIntArray();
private static final SparseIntArray INVERSE_ORIENTATIONS = new SparseIntArray();
static {
DEFAULT_ORIENTATIONS.append(Surface.ROTATION_0, 90);
DEFAULT_ORIENTATIONS.append(Surface.ROTATION_90, 0);
DEFAULT_ORIENTATIONS.append(Surface.ROTATION_180, 270);
DEFAULT_ORIENTATIONS.append(Surface.ROTATION_270, 180);
}
static {
INVERSE_ORIENTATIONS.append(Surface.ROTATION_0, 270);
INVERSE_ORIENTATIONS.append(Surface.ROTATION_90, 180);
INVERSE_ORIENTATIONS.append(Surface.ROTATION_180, 90);
INVERSE_ORIENTATIONS.append(Surface.ROTATION_270, 0);
}
//영상녹화 설정
private void setUpMediaRecorder() throws IOException {
final Activity activity = getActivity();
if(null == activity) return;
mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
if (mNextVideoAbsolutePath == null || mNextVideoAbsolutePath.isEmpty()) {
mNextVideoAbsolutePath = getVideoFilePath();
}
mMediaRecorder.setOutputFile(mNextVideoAbsolutePath);
mMediaRecorder.setVideoEncodingBitRate(10000000);
mMediaRecorder.setVideoFrameRate(30);
mMediaRecorder.setVideoSize(mVideoSize.getWidth(), mVideoSize.getHeight());
mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
int rotation = activity.getWindowManager().getDefaultDisplay().getRotation();
switch (mSensorOrientation) {
case SENSOR_ORIENTATION_DEFAULT_DEGREES:
mMediaRecorder.setOrientationHint(DEFAULT_ORIENTATIONS.get(rotation));
break;
case SENSOR_ORIENTATION_INVERSE_DEGREES:
mMediaRecorder.setOrientationHint(INVERSE_ORIENTATIONS.get(rotation));
break;
}
mMediaRecorder.prepare();
}
//파일 이름 및 저장경로를 만듭니다.
private String getVideoFilePath() {
final File dir = Environment.getExternalStorageDirectory().getAbsoluteFile();
String path = dir.getPath() + "/" + DETAIL_PATH;
File dst = new File(path);
if(!dst.exists()) dst.mkdirs();
return path + System.currentTimeMillis() + ".mp4";
}
//녹화시작
private void startRecordingVideo() {
if (null == mCameraDevice || !binding.preview.isAvailable() || null == mPreviewSize) {
return;
}
assert getActivity() != null;
try {
closePreviewSession();
setUpMediaRecorder();
SurfaceTexture texture = binding.preview.getSurfaceTexture();
assert texture != null;
mCaptureRequestBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_RECORD);
List<Surface> surfaces = new ArrayList<>();
Surface previewSurface = new Surface(texture);
surfaces.add(previewSurface);
mCaptureRequestBuilder.addTarget(previewSurface);
Surface recordSurface = mMediaRecorder.getSurface();
surfaces.add(recordSurface);
mCaptureRequestBuilder.addTarget(recordSurface);
mCameraDevice.createCaptureSession(surfaces, new CameraCaptureSession.StateCallback() {
@Override
public void onConfigured(@NonNull CameraCaptureSession session) {
mCameraCaptureSession = session;
updatePreview();
getActivity().runOnUiThread(() -> {
binding.pictureBtn.setText(R.string.stop);
mIsRecordingVideo = true;
mMediaRecorder.start();
});
}
@Override
public void onConfigureFailed(@NonNull CameraCaptureSession session) {
Activity activity = getActivity();
if (null != activity) {
Toast.makeText(activity, "Failed", Toast.LENGTH_SHORT).show();
}
}
}, mBackgroundHandler);
timer();
} catch (CameraAccessException | IOException e) {
e.printStackTrace();
}
}
//녹화 중지
private void stopRecordingVideo() {
mIsRecordingVideo = false;
binding.pictureBtn.setText(R.string.record);
mMediaRecorder.stop();
mMediaRecorder.reset();
Activity activity = getActivity();
if (null != activity) {
Toast.makeText(activity, "Video saved: " + mNextVideoAbsolutePath,
Toast.LENGTH_SHORT).show();
Log.d(TAG, "Video saved: " + mNextVideoAbsolutePath);
File file = new File(mNextVideoAbsolutePath);
// 아래 코드가 없으면 갤러리 저장 적용이 안됨.
if(!file.exists()) file.mkdir();
getActivity().getApplicationContext().sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.fromFile(file)));
}
mNextVideoAbsolutePath = null;
stop();
startPreview();
}
//카메라 전, 후, 광각 변경
// 본인 카메라에 맞게 적용하면 됨.
@Override
public void onClick(View v) {
int id = v.getId();
switch (id) {
case R.id.pictureBtn:
if (mIsRecordingVideo) stopRecordingVideo();
else startRecordingVideo();
break;
case R.id.switchImgBtn:
switch (mCamId) {
case CAM_REAR:
mCamId = CAM_FRONT;
break;
case CAM_FRONT:
mCamId = CAM_WHAT;
break;
case CAM_WHAT:
mCamId = CAM_REAR;
break;
}
closeCamera();
openCamera(binding.preview.getWidth(), binding.preview.getHeight());
break;
}
}
static class CompareSizesByArea implements Comparator<Size> {
@Override
public int compare(Size lhs, Size rhs) {
return Long.signum((long) lhs.getWidth() * lhs.getHeight() - (long) rhs.getWidth() * rhs.getHeight());
}
}
// 여기부터
//녹화시간 카운트 시작
private void timer() {
binding.recordTimeTxtView.setVisibility(View.VISIBLE);
Log.e("timer()", "started");
Observable<Long> duration = Observable.interval(1, TimeUnit.SECONDS)
.map(sec -> sec += 1);
Disposable disposable = duration.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(timeout -> {
long min = timeout / 60;
long sec = timeout % 60;
String sMin;
String sSec;
if (min < 10) sMin = "0" + min;
else sMin = String.valueOf(min);
if (sec < 10) sSec = "0" + sec;
else sSec = String.valueOf(sec);
String elapseTime = sMin + ":" + sSec;
binding.recordTimeTxtView.setText(elapseTime);
});
mCompositeDisposable.add(disposable);
}
//녹화시간 카운트 정지
private void stop() {
binding.recordTimeTxtView.setVisibility(View.GONE);
if (!mCompositeDisposable.isDisposed()) {
mCompositeDisposable.dispose();
}
}
CompositeDisposable mCompositeDisposable;
// 여기까지는 타이머 부분이기 때문에 사용안하셔도 됩니다.
@Override
public void onDestroyView() {
super.onDestroyView();
if (!mCompositeDisposable.isDisposed())
mCompositeDisposable.dispose();
}
}
호출할 수 있는 카메라의 종류를 얻는 방법.
CameraManager manager = (CameraManager) activity.getSystemService(Context.CAMERA_SERVICE);
String[] cameraId = manager.getCameraIdList();
for(int i = 0; i < cameraId.length; i++) {
Log.e("cameraId", cameraId);
}
처럼 사용하면 사용할 수 있는 카메라를 구할 수 있습니다.
저도 머리로 100% 정리가 안돼서 코드만 쭉올렸습니다.
우선 순서대로 코드를 추가하시면 영상을 녹화하고 갤러리에 저장되고 까지는
문제없이 잘 될 거에요.
안되거나 궁금하신 점들은 댓글달아주시면 답변드리겠습니다.
이상입니다. 감사합니다.
다음 포스팅에서는 동영상이 아닌 사진 촬영방법에 대해서도 포스팅하겠습니다.