From cb96601ccf0adc1ef25568ea44cf9c9bba070457 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=96=9B=E7=BF=94?= Date: Thu, 31 May 2018 11:45:01 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E4=BB=A3=E7=A0=81=E5=A4=84?= =?UTF-8?q?=E7=90=86=EF=BC=8C=E5=B0=86=E8=A7=86=E9=A2=91=E5=BD=95=E5=88=B6?= =?UTF-8?q?=E7=94=B1activity=E6=94=B9=E4=B8=BAfragment?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .idea/caches/build_file_checksums.ser | Bin 652 -> 652 bytes .../com/xuexiang/xvideodemo/MainActivity.java | 8 +- xvideo-lib/build.gradle | 1 + .../xvideo/MediaRecorderActivity.java | 718 ++--------------- .../xvideo/MediaRecorderFragment.java | 727 ++++++++++++++++++ .../xvideo/model/LocalMediaConfig.java | 28 +- .../xvideo/model/MediaRecorderConfig.java | 120 +-- .../layout/xvideo_activity_media_recorder.xml | 122 +-- .../layout/xvideo_layout_media_recorder.xml | 122 +++ 9 files changed, 1010 insertions(+), 836 deletions(-) create mode 100644 xvideo-lib/src/main/java/com/xuexiang/xvideo/MediaRecorderFragment.java create mode 100644 xvideo-lib/src/main/res/layout/xvideo_layout_media_recorder.xml diff --git a/.idea/caches/build_file_checksums.ser b/.idea/caches/build_file_checksums.ser index e1c7de61434908c1aac91b353b8f50a9e35780ef..f31138ca54512e89a099db8615a25ad41a6daed6 100644 GIT binary patch delta 36 ucmV+<0NekJ1&jrdm;|%WW4W=MgaHuBco6lIxzW)kuW$pKV+_5MFalgMu@E!> delta 36 ucmV+<0NekJ1&jrdm;|xm;!3fcgaHt$1=++T_Rcu2t^Vgi4Xl=vFalgW&k;=k diff --git a/xvideo-demo/src/main/java/com/xuexiang/xvideodemo/MainActivity.java b/xvideo-demo/src/main/java/com/xuexiang/xvideodemo/MainActivity.java index 83bc84c..52bc385 100644 --- a/xvideo-demo/src/main/java/com/xuexiang/xvideodemo/MainActivity.java +++ b/xvideo-demo/src/main/java/com/xuexiang/xvideodemo/MainActivity.java @@ -274,7 +274,7 @@ public class MainActivity extends AppCompatActivity { } // FFMpegUtils.captureThumbnails("/storage/emulated/0/DCIM/mabeijianxi/1496455533800/1496455533800.mp4", "/storage/emulated/0/DCIM/mabeijianxi/1496455533800/1496455533800.jpg", "1"); - MediaRecorderConfig config = new MediaRecorderConfig.Buidler() + MediaRecorderConfig config = new MediaRecorderConfig.Builder() .fullScreen(needFull) .smallVideoWidth(needFull ? 0 : Integer.valueOf(width)) .smallVideoHeight(Integer.valueOf(height)) @@ -284,7 +284,7 @@ public class MainActivity extends AppCompatActivity { .videoBitrate(Integer.valueOf(bitrate)) .captureThumbnailsTime(1) .build(); - MediaRecorderActivity.goSmallVideoRecorder(this, SendSmallVideoActivity.class.getName(), config); + MediaRecorderActivity.startVideoRecorder(this, SendSmallVideoActivity.class.getName(), config); } @@ -364,8 +364,8 @@ public class MainActivity extends AppCompatActivity { if (!TextUtils.isEmpty(scale)) { fScale = Float.valueOf(scale); } - LocalMediaConfig.Buidler buidler = new LocalMediaConfig.Buidler(); - final LocalMediaConfig config = buidler + LocalMediaConfig.Builder builder = new LocalMediaConfig.Builder(); + final LocalMediaConfig config = builder .setVideoPath(_data) .captureThumbnailsTime(1) .doH264Compress(compressMode) diff --git a/xvideo-lib/build.gradle b/xvideo-lib/build.gradle index 2083495..2a621a0 100644 --- a/xvideo-lib/build.gradle +++ b/xvideo-lib/build.gradle @@ -37,6 +37,7 @@ android { dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) + compileOnly deps.support.app_compat } apply from: "https://raw.githubusercontent.com/xuexiangjys/XUtil/master/JitPackUpload.gradle" diff --git a/xvideo-lib/src/main/java/com/xuexiang/xvideo/MediaRecorderActivity.java b/xvideo-lib/src/main/java/com/xuexiang/xvideo/MediaRecorderActivity.java index 8431096..e0f7725 100644 --- a/xvideo-lib/src/main/java/com/xuexiang/xvideo/MediaRecorderActivity.java +++ b/xvideo-lib/src/main/java/com/xuexiang/xvideo/MediaRecorderActivity.java @@ -1,105 +1,26 @@ package com.xuexiang.xvideo; import android.app.Activity; -import android.app.AlertDialog; -import android.app.ProgressDialog; -import android.content.DialogInterface; import android.content.Intent; import android.os.Bundle; -import android.os.Handler; -import android.os.Message; -import android.view.MotionEvent; -import android.view.SurfaceView; -import android.view.View; -import android.view.View.OnClickListener; -import android.view.Window; -import android.view.WindowManager; -import android.widget.CheckBox; -import android.widget.CheckedTextView; -import android.widget.FrameLayout; -import android.widget.ImageView; -import android.widget.RelativeLayout; -import android.widget.TextView; +import android.support.v7.app.AppCompatActivity; import android.widget.Toast; import com.xuexiang.xvideo.model.MediaObject; import com.xuexiang.xvideo.model.MediaRecorderConfig; -import java.io.File; - - /** - * 视频录制 + * 视频录制界面 + * + * @author xuexiang + * @since 2018/5/31 上午11:38 */ -public class MediaRecorderActivity extends Activity implements - MediaRecorderBase.OnErrorListener, OnClickListener, MediaRecorderBase.OnPreparedListener, - MediaRecorderBase.OnEncodeListener { - - private int RECORD_TIME_MIN = (int) (1.5f * 1000); - /** - * 录制最长时间 - */ - private int RECORD_TIME_MAX = 6 * 1000; - /** - * 刷新进度条 - */ - private static final int HANDLE_INVALIDATE_PROGRESS = 0; - /** - * 延迟拍摄停止 - */ - private static final int HANDLE_STOP_RECORD = 1; - - /** - * 下一步 - */ - private ImageView mTitleNext; - /** - * 前后摄像头切换 - */ - private CheckBox mCameraSwitch; +public class MediaRecorderActivity extends AppCompatActivity implements MediaRecorderFragment.OnMediaRecorderListener { /** - * 回删按钮、延时按钮、滤镜按钮 - */ - private CheckedTextView mRecordDelete; - /** - * 闪光灯 - */ - private CheckBox mRecordLed; - /** - * 拍摄按钮 - */ - private TextView mRecordController; - - /** - * 底部条 - */ - private RelativeLayout mBottomLayout; - /** - * 摄像头数据显示画布 - */ - private SurfaceView mSurfaceView; - /** - * 录制进度 - */ - private ProgressView mProgressView; - - /** - * SDK视频录制对象 - */ - private MediaRecorderBase mMediaRecorder; - /** - * 视频信息 + * 录制完成后需要跳转的activity */ - private MediaObject mMediaObject; + public final static String OVER_ACTIVITY_NAME = "over_activity_name"; - /** - * 是否是点击状态 - */ - private volatile boolean mPressedStatus; - /** - * 是否已经释放 - */ - private volatile boolean mReleased; /** * 视屏地址 */ @@ -112,614 +33,101 @@ public class MediaRecorderActivity extends Activity implements * 视屏截图地址 */ public final static String VIDEO_SCREENSHOT = "video_screenshot"; - /** - * 录制完成后需要跳转的activity - */ - public final static String OVER_ACTIVITY_NAME = "over_activity_name"; - /** - * 最大录制时间的key - */ - public final static String MEDIA_RECORDER_MAX_TIME_KEY = "media_recorder_max_time_key"; - /** - * 最小录制时间的key - */ - public final static String MEDIA_RECORDER_MIN_TIME_KEY = "media_recorder_min_time_key"; - /** - * 录制配置key - */ - public final static String MEDIA_RECORDER_CONFIG_KEY = "media_recorder_config_key"; - private boolean GO_HOME; - private boolean startState; - private boolean NEED_FULL_SCREEN = false; - private RelativeLayout title_layout; + private MediaRecorderFragment mMediaRecorderFragment; + + private String overActivityName = ""; /** + * 开始视频录制 * @param context * @param overGOActivityName 录制结束后需要跳转的Activity全类名 + * @param mediaRecorderConfig */ - public static void goSmallVideoRecorder(Activity context, String overGOActivityName, MediaRecorderConfig mediaRecorderConfig) { - context.startActivity(new Intent(context, MediaRecorderActivity.class).putExtra(OVER_ACTIVITY_NAME, overGOActivityName).putExtra(MEDIA_RECORDER_CONFIG_KEY, mediaRecorderConfig)); - } - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); // 防止锁屏 - initData(); - loadViews(); - } - - private void initData() { - Intent intent = getIntent(); - MediaRecorderConfig mediaRecorderConfig = intent.getParcelableExtra(MEDIA_RECORDER_CONFIG_KEY); - if (mediaRecorderConfig == null) { - return; - } - NEED_FULL_SCREEN = mediaRecorderConfig.getFullScreen(); - RECORD_TIME_MAX = mediaRecorderConfig.getRecordTimeMax(); - RECORD_TIME_MIN = mediaRecorderConfig.getRecordTimeMin(); - MediaRecorderBase.MAX_FRAME_RATE = mediaRecorderConfig.getMaxFrameRate(); - MediaRecorderBase.NEED_FULL_SCREEN = NEED_FULL_SCREEN; - MediaRecorderBase.MIN_FRAME_RATE = mediaRecorderConfig.getMinFrameRate(); - MediaRecorderBase.SMALL_VIDEO_HEIGHT = mediaRecorderConfig.getSmallVideoHeight(); - MediaRecorderBase.SMALL_VIDEO_WIDTH = mediaRecorderConfig.getSmallVideoWidth(); - MediaRecorderBase.mVideoBitrate = mediaRecorderConfig.getVideoBitrate(); - MediaRecorderBase.CAPTURE_THUMBNAILS_TIME = mediaRecorderConfig.getCaptureThumbnailsTime(); - GO_HOME = mediaRecorderConfig.isGO_HOME(); - } - - /** - * 加载视图 - */ - private void loadViews() { - setContentView(R.layout.xvideo_activity_media_recorder); - // ~~~ 绑定控件 - mSurfaceView = (SurfaceView) findViewById(R.id.record_preview); - title_layout = (RelativeLayout) findViewById(R.id.title_layout); - mCameraSwitch = (CheckBox) findViewById(R.id.record_camera_switcher); - mTitleNext = (ImageView) findViewById(R.id.title_next); - mProgressView = (ProgressView) findViewById(R.id.record_progress); - mRecordDelete = (CheckedTextView) findViewById(R.id.record_delete); - mRecordController = (TextView) findViewById(R.id.record_controller); - mBottomLayout = (RelativeLayout) findViewById(R.id.bottom_layout); - mRecordLed = (CheckBox) findViewById(R.id.record_camera_led); - - // ~~~ 绑定事件 - /*if (DeviceUtils.hasICS()) - mSurfaceView.setOnTouchListener(mOnSurfaveViewTouchListener);*/ - - mTitleNext.setOnClickListener(this); - findViewById(R.id.title_back).setOnClickListener(this); -// mRecordDelete.setOnClickListener(this); - mRecordController.setOnTouchListener(mOnVideoControllerTouchListener); - - // ~~~ 设置数据 - - // 是否支持前置摄像头 - if (MediaRecorderBase.isSupportFrontCamera()) { - mCameraSwitch.setOnClickListener(this); - } else { - mCameraSwitch.setVisibility(View.GONE); - } - // 是否支持闪光灯 - if (DeviceUtils.isSupportCameraLedFlash(getPackageManager())) { - mRecordLed.setOnClickListener(this); - } else { - mRecordLed.setVisibility(View.GONE); - } - - - mProgressView.setMaxDuration(RECORD_TIME_MAX); - mProgressView.setMinTime(RECORD_TIME_MIN); + public static void startVideoRecorder(Activity context, String overGOActivityName, MediaRecorderConfig mediaRecorderConfig) { + context.startActivity(new Intent(context, MediaRecorderActivity.class) + .putExtra(OVER_ACTIVITY_NAME, overGOActivityName) + .putExtra(MediaRecorderFragment.MEDIA_RECORDER_CONFIG_KEY, mediaRecorderConfig)); } /** - * 初始化画布 - */ - private void initSurfaceView() { - if (NEED_FULL_SCREEN) { - mBottomLayout.setBackgroundColor(0); - title_layout.setBackgroundColor(getResources().getColor(R.color.xvideo_full_title_color)); - FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mSurfaceView - .getLayoutParams(); - lp.setMargins(0, 0, 0, 0); - mSurfaceView.setLayoutParams(lp); - mProgressView.setBackgroundColor(getResources().getColor(R.color.xvideo_full_progress_color)); - } else { - final int w = DeviceUtils.getScreenWidth(this); - ((RelativeLayout.LayoutParams) mBottomLayout.getLayoutParams()).topMargin = (int) (w / (MediaRecorderBase.SMALL_VIDEO_HEIGHT / (MediaRecorderBase.SMALL_VIDEO_WIDTH * 1.0f))); - int width = w; - int height = (int) (w * ((MediaRecorderBase.mSupportedPreviewWidth * 1.0f) / MediaRecorderBase.SMALL_VIDEO_HEIGHT)); - // - FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mSurfaceView - .getLayoutParams(); - lp.width = width; - lp.height = height; - mSurfaceView.setLayoutParams(lp); - } - } - - /** - * 初始化拍摄SDK + * 开始视频录制 + * @param context + * @param mediaRecorderConfig */ - private void initMediaRecorder() { - mMediaRecorder = new MediaRecorderNative(); - - mMediaRecorder.setOnErrorListener(this); - mMediaRecorder.setOnEncodeListener(this); - mMediaRecorder.setOnPreparedListener(this); - - File f = new File(XCamera.getVideoCachePath()); - if (!FileUtils.checkFile(f)) { - f.mkdirs(); - } - String key = String.valueOf(System.currentTimeMillis()); - mMediaObject = mMediaRecorder.setOutputDirectory(key, - XCamera.getVideoCachePath() + key); - mMediaRecorder.setSurfaceHolder(mSurfaceView.getHolder()); - mMediaRecorder.prepare(); + public static void startVideoRecorder(Activity context, MediaRecorderConfig mediaRecorderConfig) { + context.startActivity(new Intent(context, MediaRecorderActivity.class) + .putExtra(MediaRecorderFragment.MEDIA_RECORDER_CONFIG_KEY, mediaRecorderConfig)); } - - /** - * 点击屏幕录制 - */ - private View.OnTouchListener mOnVideoControllerTouchListener = new View.OnTouchListener() { - - @Override - public boolean onTouch(View v, MotionEvent event) { - if (mMediaRecorder == null) { - return false; - } - - switch (event.getAction()) { - case MotionEvent.ACTION_DOWN: - // 检测是否手动对焦 - // 判断是否已经超时 - if (mMediaObject.getDuration() >= RECORD_TIME_MAX) { - return true; - } - - // 取消回删 - if (cancelDelete()) - return true; - if (!startState) { - startState = true; - startRecord(); - } else { - mMediaObject.buildMediaPart(mMediaRecorder.mCameraId); - mProgressView.setData(mMediaObject); - setStartUI(); - mMediaRecorder.setRecordState(true); - } - - break; - - case MotionEvent.ACTION_UP: - - mMediaRecorder.setRecordState(false); - if (mMediaObject.getDuration() >= RECORD_TIME_MAX) { - mTitleNext.performClick(); - } else { - mMediaRecorder.setStopDate(); - setStopUI(); - } - - - // 暂停 -/* if (mPressedStatus) { - - // 检测是否已经完成 - if (mMediaObject.getDuration() >= RECORD_TIME_MAX) { - mTitleNext.performClick(); - } - }*/ - break; - } - return true; - } - - }; - @Override - public void onResume() { - super.onResume(); - - if (mMediaRecorder == null) { - initMediaRecorder(); - } else { - mRecordLed.setChecked(false); - mMediaRecorder.prepare(); - mProgressView.setData(mMediaObject); - } - } - - /*@Override - public void onPause() { - super.onPause(); - stopRecord(); - if (!mReleased) { - if (mMediaRecorder != null) - mMediaRecorder.release(); - } - mReleased = false; - }*/ - - - /** - * 开始录制 - */ - private void startRecord() { - if (mMediaRecorder != null) { + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + MediaRecorderFragment.onCreateMediaRecorder(this); + setContentView(R.layout.xvideo_activity_media_recorder); - MediaObject.MediaPart part = mMediaRecorder.startRecord(); - if (part == null) { - return; + Intent intent = getIntent(); + if (intent != null) { + MediaRecorderConfig mediaRecorderConfig = intent.getParcelableExtra(MediaRecorderFragment.MEDIA_RECORDER_CONFIG_KEY); + overActivityName = intent.getStringExtra(OVER_ACTIVITY_NAME); + if (mediaRecorderConfig == null) { + mediaRecorderConfig = MediaRecorderConfig.newInstance(); } - mProgressView.setData(mMediaObject); - } - - setStartUI(); - } - - private void setStartUI() { - mPressedStatus = true; -// TODO 开始录制的图标 - mRecordController.animate().scaleX(0.8f).scaleY(0.8f).setDuration(500).start(); + mMediaRecorderFragment = MediaRecorderFragment.newInstance(mediaRecorderConfig, this); - - if (mHandler != null) { - mHandler.removeMessages(HANDLE_INVALIDATE_PROGRESS); - mHandler.sendEmptyMessage(HANDLE_INVALIDATE_PROGRESS); - - mHandler.removeMessages(HANDLE_STOP_RECORD); - mHandler.sendEmptyMessageDelayed(HANDLE_STOP_RECORD, - RECORD_TIME_MAX - mMediaObject.getDuration()); + getSupportFragmentManager().beginTransaction().replace(R.id.fl_record_container, mMediaRecorderFragment).commit(); } -// mRecordDelete.setVisibility(View.GONE); - mCameraSwitch.setEnabled(false); - mRecordLed.setEnabled(false); } @Override public void onBackPressed() { - /*if (mRecordDelete != null && mRecordDelete.isChecked()) { - cancelDelete(); - return; - }*/ - - if (mMediaObject != null && mMediaObject.getDuration() > 1) { - // 未转码 - new AlertDialog.Builder(this) - .setTitle(R.string.xvideo_hint) - .setMessage(R.string.xvideo_camera_exit_dialog_message) - .setNegativeButton( - R.string.xvideo_camera_cancel_dialog_yes, - new DialogInterface.OnClickListener() { - - @Override - public void onClick(DialogInterface dialog, - int which) { - mMediaObject.delete(); - finish(); - } - - }) - .setPositiveButton(R.string.xvideo_camera_cancel_dialog_no, - null).setCancelable(false).show(); - return; - } - - if (mMediaObject != null) - mMediaObject.delete(); - finish(); + mMediaRecorderFragment.onBackPressed(); } /** - * 停止录制 + * 取消视频录制 */ - private void stopRecord() { - if (mMediaRecorder != null) { - mMediaRecorder.stopRecord(); - } - setStopUI(); - } - - private void setStopUI() { - mPressedStatus = false; - mRecordController.animate().scaleX(1).scaleY(1).setDuration(500).start(); - - -// mRecordDelete.setVisibility(View.VISIBLE); - mCameraSwitch.setEnabled(true); - mRecordLed.setEnabled(true); - - mHandler.removeMessages(HANDLE_STOP_RECORD); - checkStatus(); - } - @Override - public void onClick(View v) { - final int id = v.getId(); - if (mHandler.hasMessages(HANDLE_STOP_RECORD)) { - mHandler.removeMessages(HANDLE_STOP_RECORD); - } - - // 处理开启回删后其他点击操作 - if (id != R.id.record_delete) { - if (mMediaObject != null) { - MediaObject.MediaPart part = mMediaObject.getCurrentPart(); - if (part != null) { - if (part.remove) { - part.remove = false; -// mRecordDelete.setChecked(false); - if (mProgressView != null) - mProgressView.invalidate(); - } - } - } - } - - if (id == R.id.title_back) { - onBackPressed(); - } else if (id == R.id.record_camera_switcher) {// 前后摄像头切换 - if (mRecordLed.isChecked()) { - if (mMediaRecorder != null) { - mMediaRecorder.toggleFlashMode(); - } - mRecordLed.setChecked(false); - } - - if (mMediaRecorder != null) { - mMediaRecorder.switchCamera(); - } - - if (mMediaRecorder.isFrontCamera()) { - mRecordLed.setEnabled(false); - } else { - mRecordLed.setEnabled(true); - } - } else if (id == R.id.record_camera_led) {// 闪光灯 - // 开启前置摄像头以后不支持开启闪光灯 - if (mMediaRecorder != null) { - if (mMediaRecorder.isFrontCamera()) { - return; - } - } - - if (mMediaRecorder != null) { - mMediaRecorder.toggleFlashMode(); - } - } else if (id == R.id.title_next) {// 停止录制 - stopRecord(); - /*finish(); - overridePendingTransition(R.anim.push_bottom_in, - R.anim.push_bottom_out);*/ - } else if (id == R.id.record_delete) { - // 取消回删 - if (mMediaObject != null) { - MediaObject.MediaPart part = mMediaObject.getCurrentPart(); - if (part != null) { - if (part.remove) { - part.remove = false; - mMediaObject.removePart(part, true); -// mRecordDelete.setChecked(false); - } else { - part.remove = true; -// mRecordDelete.setChecked(true); - } - } - if (mProgressView != null) - mProgressView.invalidate(); - - // 检测按钮状态 - checkStatus(); - } - } - } - - - /** - * 取消回删 - */ - private boolean cancelDelete() { - if (mMediaObject != null) { - MediaObject.MediaPart part = mMediaObject.getCurrentPart(); - if (part != null && part.remove) { - part.remove = false; -// mRecordDelete.setChecked(false); - - if (mProgressView != null) - mProgressView.invalidate(); - - return true; - } - } - return false; - } - - /** - * 检查录制时间,显示/隐藏下一步按钮 - */ - private int checkStatus() { - int duration = 0; - if (!isFinishing() && mMediaObject != null) { - duration = mMediaObject.getDuration(); - if (duration < RECORD_TIME_MIN) { - if (duration == 0) { - mCameraSwitch.setVisibility(View.VISIBLE); -// mRecordDelete.setVisibility(View.GONE); - } else { - mCameraSwitch.setVisibility(View.GONE); - } - // 视频必须大于3秒 - if (mTitleNext.getVisibility() != View.INVISIBLE) - mTitleNext.setVisibility(View.INVISIBLE); - } else { - // 下一步 - if (mTitleNext.getVisibility() != View.VISIBLE) { - mTitleNext.setVisibility(View.VISIBLE); - } - } - } - return 0; - } - -// private Handler mHandler = new Handler() { -// @Override -// public void handleMessage(Message msg) { -// switch (msg.what) { -// case HANDLE_INVALIDATE_PROGRESS: -// if (mMediaRecorder != null && !isFinishing()) { -// if (mMediaObject != null && mMediaObject.getMedaParts() != null && mMediaObject.getDuration() >= RECORD_TIME_MAX) { -// mTitleNext.performClick(); -// return; -// } -// if (mProgressView != null) -// mProgressView.invalidate(); -// // if (mPressedStatus) -// // titleText.setText(String.format("%.1f", -// // mMediaRecorder.getDuration() / 1000F)); -// if (mPressedStatus) -// sendEmptyMessageDelayed(0, 30); -// } -// break; -// } -// } -// }; - - private Handler mHandler = new Handler(new Handler.Callback() { - @Override - public boolean handleMessage(Message msg) { - switch (msg.what) { - case HANDLE_INVALIDATE_PROGRESS: - if (mMediaRecorder != null && !isFinishing()) { - if (mMediaObject != null && mMediaObject.getMedaParts() != null && mMediaObject.getDuration() >= RECORD_TIME_MAX) { - mTitleNext.performClick(); - return true; - } - if (mProgressView != null) { - mProgressView.invalidate(); - } - if (mPressedStatus) { - mHandler.sendEmptyMessageDelayed(0, 30); - } - } - return true; - } - return true; - } - }); - - @Override - public void onEncodeStart() { - showProgress("", getString(R.string.xvideo_camera_progress_message)); - } - - @Override - public void onEncodeProgress(int progress) { + public void onCancel() { + finish(); } /** - * 转码完成 + * 视频录制成功 + * + * @param mediaObject 录制视频的信息 */ @Override - public void onEncodeComplete() { - hideProgress(); - Intent intent = null; - try { - intent = new Intent(this, Class.forName(getIntent().getStringExtra(OVER_ACTIVITY_NAME))); - intent.putExtra(MediaRecorderActivity.OUTPUT_DIRECTORY, mMediaObject.getOutputDirectory()); - intent.putExtra(MediaRecorderActivity.VIDEO_URI, mMediaObject.getOutputTempTranscodingVideoPath()); - intent.putExtra(MediaRecorderActivity.VIDEO_SCREENSHOT, mMediaObject.getOutputVideoThumbPath()); - intent.putExtra("go_home", GO_HOME); + public void onRecordSuccess(MediaObject mediaObject) { + Intent intent = new Intent(); + Bundle bundle = new Bundle(); + bundle.putString(MediaRecorderActivity.OUTPUT_DIRECTORY, mediaObject.getOutputDirectory()); + bundle.putString(MediaRecorderActivity.VIDEO_URI, mediaObject.getOutputTempTranscodingVideoPath()); + bundle.putString(MediaRecorderActivity.VIDEO_SCREENSHOT, mediaObject.getOutputVideoThumbPath()); + bundle.putString(MediaRecorderActivity.OUTPUT_DIRECTORY, mediaObject.getOutputDirectory()); + intent.putExtras(bundle); + if (StringUtils.isEmpty(overActivityName)) { + setResult(RESULT_OK, intent); + } else { + try { + intent.setClass(this, Class.forName(overActivityName)); + } catch (ClassNotFoundException e) { + throw new IllegalArgumentException("需要传入录制完成后跳转的Activity的全类名"); + } startActivity(intent); - } catch (ClassNotFoundException e) { - throw new IllegalArgumentException("需要传入录制完成后跳转的Activity的全类名"); } - finish(); } /** - * 转码失败 检查sdcard是否可用,检查分块是否存在 + * 视频录制失败 + * + * @param msg 失败原因 */ @Override - public void onEncodeError() { - hideProgress(); - Toast.makeText(this, R.string.xvideo_video_transcoding_faild, - Toast.LENGTH_SHORT).show(); - finish(); - } - - @Override - public void onVideoError(int what, int extra) { - - } - - @Override - public void onAudioError(int what, String message) { - - } - - @Override - public void onPrepared() { - initSurfaceView(); - } - - public void onFinished() { + public void onRecordFailed(String msg) { + Toast.makeText(this, msg, Toast.LENGTH_LONG).show(); finish(); } - - protected ProgressDialog mProgressDialog; - - public ProgressDialog showProgress(String title, String message) { - return showProgress(title, message, -1); - } - - public ProgressDialog showProgress(String title, String message, int theme) { - if (mProgressDialog == null) { - if (theme > 0) - mProgressDialog = new ProgressDialog(this, theme); - else - mProgressDialog = new ProgressDialog(this); - mProgressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER); - mProgressDialog.requestWindowFeature(Window.FEATURE_NO_TITLE); - mProgressDialog.setCanceledOnTouchOutside(false);// 不能取消 - mProgressDialog.setCancelable(false); - mProgressDialog.setIndeterminate(true);// 设置进度条是否不明确 - } - - if (!StringUtils.isEmpty(title)) - mProgressDialog.setTitle(title); - mProgressDialog.setMessage(message); - mProgressDialog.show(); - return mProgressDialog; - } - - public void hideProgress() { - if (mProgressDialog != null) { - mProgressDialog.dismiss(); - } - } - - @Override - protected void onDestroy() { - super.onDestroy(); - mMediaRecorder.release(); - } - - @Override - protected void onStop() { - super.onStop(); - if (mMediaRecorder instanceof MediaRecorderNative) { - ((MediaRecorderNative) mMediaRecorder).activityStop(); - } - hideProgress(); - mProgressDialog = null; - - } - } diff --git a/xvideo-lib/src/main/java/com/xuexiang/xvideo/MediaRecorderFragment.java b/xvideo-lib/src/main/java/com/xuexiang/xvideo/MediaRecorderFragment.java new file mode 100644 index 0000000..7761654 --- /dev/null +++ b/xvideo-lib/src/main/java/com/xuexiang/xvideo/MediaRecorderFragment.java @@ -0,0 +1,727 @@ +package com.xuexiang.xvideo; + +import android.app.Activity; +import android.app.AlertDialog; +import android.app.ProgressDialog; +import android.content.DialogInterface; +import android.graphics.PixelFormat; +import android.os.Bundle; +import android.os.Handler; +import android.os.Message; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v4.app.Fragment; +import android.view.LayoutInflater; +import android.view.MotionEvent; +import android.view.SurfaceView; +import android.view.View; +import android.view.ViewGroup; +import android.view.Window; +import android.view.WindowManager; +import android.widget.CheckBox; +import android.widget.CheckedTextView; +import android.widget.FrameLayout; +import android.widget.ImageView; +import android.widget.RelativeLayout; +import android.widget.TextView; + +import com.xuexiang.xvideo.model.MediaObject; +import com.xuexiang.xvideo.model.MediaRecorderConfig; + +import java.io.File; + +import static com.xuexiang.xvideo.model.MediaRecorderConfig.DEFAULT_RECORD_TIME_MAX; +import static com.xuexiang.xvideo.model.MediaRecorderConfig.DEFAULT_RECORD_TIME_MIN; + +/** + * 视频录制的fragment + * + * @author xuexiang + * @since 2018/5/31 上午9:27 + */ +public class MediaRecorderFragment extends Fragment implements + MediaRecorderBase.OnErrorListener, View.OnClickListener, MediaRecorderBase.OnPreparedListener, + MediaRecorderBase.OnEncodeListener { + + /** + * 录制最短时间 + */ + private int recordTimeMin = DEFAULT_RECORD_TIME_MIN; + /** + * 录制最长时间 + */ + private int recordTimeMax = DEFAULT_RECORD_TIME_MAX; + /** + * 刷新进度条 + */ + private static final int HANDLE_INVALIDATE_PROGRESS = 0; + /** + * 延迟拍摄停止 + */ + private static final int HANDLE_STOP_RECORD = 1; + + /** + * 下一步 + */ + private ImageView mTitleNext; + /** + * 前后摄像头切换 + */ + private CheckBox mCameraSwitch; + /** + * 回删按钮、延时按钮、滤镜按钮 + */ + private CheckedTextView mRecordDelete; + /** + * 闪光灯 + */ + private CheckBox mRecordLed; + /** + * 拍摄按钮 + */ + private TextView mRecordController; + + /** + * 底部条 + */ + private RelativeLayout mBottomLayout; + /** + * 摄像头数据显示画布 + */ + private SurfaceView mSurfaceView; + /** + * 录制进度 + */ + private ProgressView mProgressView; + + /** + * SDK视频录制对象 + */ + private MediaRecorderBase mMediaRecorder; + /** + * 视频信息 + */ + private MediaObject mMediaObject; + + /** + * 是否是点击状态 + */ + private volatile boolean mPressedStatus; + /** + * 录制配置key + */ + public final static String MEDIA_RECORDER_CONFIG_KEY = "media_recorder_config_key"; + + private boolean startState; + private boolean needFullScreen = false; + private RelativeLayout mTitleLayout; + + /** + * 视频录制的监听 + */ + private OnMediaRecorderListener mOnMediaRecorderListener; + + /** + * 创建视频录制的fragment + * + * @param mediaRecorderConfig + * @return + */ + public static MediaRecorderFragment newInstance(MediaRecorderConfig mediaRecorderConfig) { + MediaRecorderFragment fragment = new MediaRecorderFragment(); + Bundle bundle = new Bundle(); + bundle.putParcelable(MEDIA_RECORDER_CONFIG_KEY, mediaRecorderConfig); + fragment.setArguments(bundle); + return fragment; + } + + /** + * 创建视频录制的fragment + * + * @param mediaRecorderConfig + * @return + */ + public static MediaRecorderFragment newInstance(MediaRecorderConfig mediaRecorderConfig, OnMediaRecorderListener onMediaRecorderListener) { + MediaRecorderFragment fragment = new MediaRecorderFragment(); + Bundle bundle = new Bundle(); + bundle.putParcelable(MEDIA_RECORDER_CONFIG_KEY, mediaRecorderConfig); + fragment.setOnMediaRecorderListener(onMediaRecorderListener); + fragment.setArguments(bundle); + return fragment; + } + + /** + * 设置视频录制的监听 + * + * @param onMediaRecorderListener + * @return + */ + public MediaRecorderFragment setOnMediaRecorderListener(OnMediaRecorderListener onMediaRecorderListener) { + mOnMediaRecorderListener = onMediaRecorderListener; + return this; + } + + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + onCreateMediaRecorder(getActivity()); + } + + /** + * 处理Activity【防止锁屏和fragment里面放surfaceview,第一次黑屏的问题】 + * + * @param activity + */ + public static void onCreateMediaRecorder(Activity activity) { + if (activity != null) { + // 防止锁屏 + activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); + //为了解决fragment里面放surfaceview,第一次黑屏的问题 + activity.getWindow().setFormat(PixelFormat.TRANSLUCENT); + } + } + + @Nullable + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + View view = inflater.inflate(R.layout.xvideo_layout_media_recorder, container, false); + initArgs(); + initViews(view); + return view; + } + + private void initArgs() { + Bundle bundle = getArguments(); + if (bundle != null) { + MediaRecorderConfig mediaRecorderConfig = bundle.getParcelable(MEDIA_RECORDER_CONFIG_KEY); + if (mediaRecorderConfig == null) { + return; + } + needFullScreen = mediaRecorderConfig.getFullScreen(); + recordTimeMax = mediaRecorderConfig.getRecordTimeMax(); + recordTimeMin = mediaRecorderConfig.getRecordTimeMin(); + MediaRecorderBase.MAX_FRAME_RATE = mediaRecorderConfig.getMaxFrameRate(); + MediaRecorderBase.NEED_FULL_SCREEN = needFullScreen; + MediaRecorderBase.MIN_FRAME_RATE = mediaRecorderConfig.getMinFrameRate(); + MediaRecorderBase.SMALL_VIDEO_HEIGHT = mediaRecorderConfig.getSmallVideoHeight(); + MediaRecorderBase.SMALL_VIDEO_WIDTH = mediaRecorderConfig.getSmallVideoWidth(); + MediaRecorderBase.mVideoBitrate = mediaRecorderConfig.getVideoBitrate(); + MediaRecorderBase.CAPTURE_THUMBNAILS_TIME = mediaRecorderConfig.getCaptureThumbnailsTime(); + } + } + + /** + * 加载视图 + */ + private void initViews(View view) { + // ~~~ 绑定控件 + mSurfaceView = view.findViewById(R.id.record_preview); + mTitleLayout = view.findViewById(R.id.title_layout); + mCameraSwitch = view.findViewById(R.id.record_camera_switcher); + mTitleNext = view.findViewById(R.id.title_next); + mProgressView = view.findViewById(R.id.record_progress); + mRecordDelete = view.findViewById(R.id.record_delete); + mRecordController = view.findViewById(R.id.record_controller); + mBottomLayout = view.findViewById(R.id.bottom_layout); + mRecordLed = view.findViewById(R.id.record_camera_led); + + mTitleNext.setOnClickListener(this); + view.findViewById(R.id.title_back).setOnClickListener(this); + mRecordController.setOnTouchListener(mOnVideoControllerTouchListener); + + // 是否支持前置摄像头 + if (MediaRecorderBase.isSupportFrontCamera()) { + mCameraSwitch.setOnClickListener(this); + } else { + mCameraSwitch.setVisibility(View.GONE); + } + // 是否支持闪光灯 + if (DeviceUtils.isSupportCameraLedFlash(getContext().getPackageManager())) { + mRecordLed.setOnClickListener(this); + } else { + mRecordLed.setVisibility(View.GONE); + } + + mProgressView.setMaxDuration(recordTimeMax); + mProgressView.setMinTime(recordTimeMin); + } + + /** + * 初始化画布 + */ + private void initSurfaceView() { + if (needFullScreen) { + mBottomLayout.setBackgroundColor(0); + mTitleLayout.setBackgroundColor(getResources().getColor(R.color.xvideo_full_title_color)); + FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mSurfaceView + .getLayoutParams(); + lp.setMargins(0, 0, 0, 0); + mSurfaceView.setLayoutParams(lp); + mProgressView.setBackgroundColor(getResources().getColor(R.color.xvideo_full_progress_color)); + } else { + final int w = DeviceUtils.getScreenWidth(getContext()); + ((RelativeLayout.LayoutParams) mBottomLayout.getLayoutParams()).topMargin = (int) (w / (MediaRecorderBase.SMALL_VIDEO_HEIGHT / (MediaRecorderBase.SMALL_VIDEO_WIDTH * 1.0f))); + int width = w; + int height = (int) (w * ((MediaRecorderBase.mSupportedPreviewWidth * 1.0f) / MediaRecorderBase.SMALL_VIDEO_HEIGHT)); + // + FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mSurfaceView + .getLayoutParams(); + lp.width = width; + lp.height = height; + mSurfaceView.setLayoutParams(lp); + } + } + + /** + * 初始化拍摄SDK + */ + private void initMediaRecorder() { + mMediaRecorder = new MediaRecorderNative(); + + mMediaRecorder.setOnErrorListener(this); + mMediaRecorder.setOnEncodeListener(this); + mMediaRecorder.setOnPreparedListener(this); + + File f = new File(XCamera.getVideoCachePath()); + if (!FileUtils.checkFile(f)) { + f.mkdirs(); + } + String key = String.valueOf(System.currentTimeMillis()); + mMediaObject = mMediaRecorder.setOutputDirectory(key, + XCamera.getVideoCachePath() + key); + mMediaRecorder.setSurfaceHolder(mSurfaceView.getHolder()); + mMediaRecorder.prepare(); + } + + + /** + * 点击屏幕录制 + */ + private View.OnTouchListener mOnVideoControllerTouchListener = new View.OnTouchListener() { + + @Override + public boolean onTouch(View v, MotionEvent event) { + if (mMediaRecorder == null) { + return false; + } + + switch (event.getAction()) { + case MotionEvent.ACTION_DOWN: + // 检测是否手动对焦 + // 判断是否已经超时 + if (mMediaObject.getDuration() >= recordTimeMax) { + return true; + } + + // 取消回删 + if (cancelDelete()) + return true; + if (!startState) { + startState = true; + startRecord(); + } else { + mMediaObject.buildMediaPart(mMediaRecorder.mCameraId); + mProgressView.setData(mMediaObject); + setStartUI(); + mMediaRecorder.setRecordState(true); + } + + break; + + case MotionEvent.ACTION_UP: + + mMediaRecorder.setRecordState(false); + if (mMediaObject.getDuration() >= recordTimeMax) { + mTitleNext.performClick(); + } else { + mMediaRecorder.setStopDate(); + setStopUI(); + } + break; + } + return true; + } + + }; + + @Override + public void onResume() { + super.onResume(); + if (mMediaRecorder == null) { + initMediaRecorder(); + } else { + mRecordLed.setChecked(false); + mMediaRecorder.prepare(); + mProgressView.setData(mMediaObject); + } + } + + /** + * 开始录制 + */ + private void startRecord() { + if (mMediaRecorder != null) { + + MediaObject.MediaPart part = mMediaRecorder.startRecord(); + if (part == null) { + return; + } + + mProgressView.setData(mMediaObject); + } + + setStartUI(); + } + + private void setStartUI() { + mPressedStatus = true; +// TODO 开始录制的图标 + mRecordController.animate().scaleX(0.8f).scaleY(0.8f).setDuration(500).start(); + + + if (mHandler != null) { + mHandler.removeMessages(HANDLE_INVALIDATE_PROGRESS); + mHandler.sendEmptyMessage(HANDLE_INVALIDATE_PROGRESS); + + mHandler.removeMessages(HANDLE_STOP_RECORD); + mHandler.sendEmptyMessageDelayed(HANDLE_STOP_RECORD, + recordTimeMax - mMediaObject.getDuration()); + } + mCameraSwitch.setEnabled(false); + mRecordLed.setEnabled(false); + } + + public void onBackPressed() { + if (mMediaObject != null && mMediaObject.getDuration() > 1) { + // 未转码 + new AlertDialog.Builder(getContext()) + .setTitle(R.string.xvideo_hint) + .setMessage(R.string.xvideo_camera_exit_dialog_message) + .setNegativeButton( + R.string.xvideo_camera_cancel_dialog_yes, + new DialogInterface.OnClickListener() { + + @Override + public void onClick(DialogInterface dialog, + int which) { + mMediaObject.delete(); + onCancel(); + } + + }) + .setPositiveButton(R.string.xvideo_camera_cancel_dialog_no, + null).setCancelable(false).show(); + return; + } + + if (mMediaObject != null) { + mMediaObject.delete(); + } + onCancel(); + } + + /** + * 停止录制 + */ + private void stopRecord() { + if (mMediaRecorder != null) { + mMediaRecorder.stopRecord(); + } + setStopUI(); + } + + private void setStopUI() { + mPressedStatus = false; + mRecordController.animate().scaleX(1).scaleY(1).setDuration(500).start(); + + mCameraSwitch.setEnabled(true); + mRecordLed.setEnabled(true); + + mHandler.removeMessages(HANDLE_STOP_RECORD); + checkStatus(); + } + + @Override + public void onClick(View v) { + final int id = v.getId(); + if (mHandler.hasMessages(HANDLE_STOP_RECORD)) { + mHandler.removeMessages(HANDLE_STOP_RECORD); + } + + // 处理开启回删后其他点击操作 + if (id != R.id.record_delete) { + if (mMediaObject != null) { + MediaObject.MediaPart part = mMediaObject.getCurrentPart(); + if (part != null) { + if (part.remove) { + part.remove = false; + if (mProgressView != null) { + mProgressView.invalidate(); + } + } + } + } + } + + if (id == R.id.title_back) { + onBackPressed(); + } else if (id == R.id.record_camera_switcher) {// 前后摄像头切换 + if (mRecordLed.isChecked()) { + if (mMediaRecorder != null) { + mMediaRecorder.toggleFlashMode(); + } + mRecordLed.setChecked(false); + } + + if (mMediaRecorder != null) { + mMediaRecorder.switchCamera(); + } + + if (mMediaRecorder.isFrontCamera()) { + mRecordLed.setEnabled(false); + } else { + mRecordLed.setEnabled(true); + } + } else if (id == R.id.record_camera_led) {// 闪光灯 + // 开启前置摄像头以后不支持开启闪光灯 + if (mMediaRecorder != null) { + if (mMediaRecorder.isFrontCamera()) { + return; + } + } + + if (mMediaRecorder != null) { + mMediaRecorder.toggleFlashMode(); + } + } else if (id == R.id.title_next) {// 停止录制 + stopRecord(); + } else if (id == R.id.record_delete) { + // 取消回删 + if (mMediaObject != null) { + MediaObject.MediaPart part = mMediaObject.getCurrentPart(); + if (part != null) { + if (part.remove) { + part.remove = false; + mMediaObject.removePart(part, true); + } else { + part.remove = true; + } + } + if (mProgressView != null) + mProgressView.invalidate(); + + // 检测按钮状态 + checkStatus(); + } + } + } + + + /** + * 取消回删 + */ + private boolean cancelDelete() { + if (mMediaObject != null) { + MediaObject.MediaPart part = mMediaObject.getCurrentPart(); + if (part != null && part.remove) { + part.remove = false; + if (mProgressView != null) { + mProgressView.invalidate(); + } + return true; + } + } + return false; + } + + /** + * 检查录制时间,显示/隐藏下一步按钮 + */ + private int checkStatus() { + int duration = 0; + if (!getActivity().isFinishing() && mMediaObject != null) { + duration = mMediaObject.getDuration(); + if (duration < recordTimeMin) { + if (duration == 0) { + mCameraSwitch.setVisibility(View.VISIBLE); + } else { + mCameraSwitch.setVisibility(View.GONE); + } + // 视频必须大于3秒 + if (mTitleNext.getVisibility() != View.INVISIBLE) + mTitleNext.setVisibility(View.INVISIBLE); + } else { + // 下一步 + if (mTitleNext.getVisibility() != View.VISIBLE) { + mTitleNext.setVisibility(View.VISIBLE); + } + } + } + return 0; + } + + private Handler mHandler = new Handler(new Handler.Callback() { + @Override + public boolean handleMessage(Message msg) { + switch (msg.what) { + case HANDLE_INVALIDATE_PROGRESS: + if (mMediaRecorder != null && !getActivity().isFinishing()) { + if (mMediaObject != null && mMediaObject.getMedaParts() != null && mMediaObject.getDuration() >= recordTimeMax) { + mTitleNext.performClick(); + return true; + } + if (mProgressView != null) { + mProgressView.invalidate(); + } + if (mPressedStatus) { + mHandler.sendEmptyMessageDelayed(0, 30); + } + } + return true; + } + return true; + } + }); + + @Override + public void onEncodeStart() { + showProgress("", getString(R.string.xvideo_camera_progress_message)); + } + + @Override + public void onEncodeProgress(int progress) { + } + + /** + * 转码完成 + */ + @Override + public void onEncodeComplete() { + hideProgress(); + onRecordSuccess(mMediaObject); + } + + /** + * 转码失败 检查sdcard是否可用,检查分块是否存在 + */ + @Override + public void onEncodeError() { + hideProgress(); + onRecordFailed(getString(R.string.xvideo_video_transcoding_faild)); + } + + @Override + public void onVideoError(int what, int extra) { + + } + + @Override + public void onAudioError(int what, String message) { + + } + + @Override + public void onPrepared() { + initSurfaceView(); + } + + protected ProgressDialog mProgressDialog; + + public ProgressDialog showProgress(String title, String message) { + return showProgress(title, message, -1); + } + + public ProgressDialog showProgress(String title, String message, int theme) { + if (mProgressDialog == null) { + if (theme > 0) { + mProgressDialog = new ProgressDialog(getContext(), theme); + } else { + mProgressDialog = new ProgressDialog(getContext()); + } + mProgressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER); + mProgressDialog.requestWindowFeature(Window.FEATURE_NO_TITLE); + mProgressDialog.setCanceledOnTouchOutside(false);// 不能取消 + mProgressDialog.setCancelable(false); + mProgressDialog.setIndeterminate(true);// 设置进度条是否不明确 + } + + if (!StringUtils.isEmpty(title)) { + mProgressDialog.setTitle(title); + } + mProgressDialog.setMessage(message); + mProgressDialog.show(); + return mProgressDialog; + } + + public void hideProgress() { + if (mProgressDialog != null) { + mProgressDialog.dismiss(); + } + } + + @Override + public void onDestroyView() { + if (mMediaRecorder != null) { + mMediaRecorder.release(); + } + super.onDestroyView(); + } + + @Override + public void onStop() { + if (mMediaRecorder != null) { + if (mMediaRecorder instanceof MediaRecorderNative) { + ((MediaRecorderNative) mMediaRecorder).activityStop(); + } + hideProgress(); + mProgressDialog = null; + } + super.onStop(); + } + + private void onCancel() { + if (mOnMediaRecorderListener != null) { + mOnMediaRecorderListener.onCancel(); + } + } + + private void onRecordSuccess(MediaObject mediaObject) { + if (mOnMediaRecorderListener != null) { + mOnMediaRecorderListener.onRecordSuccess(mediaObject); + } + } + + private void onRecordFailed(String msg) { + if (mOnMediaRecorderListener != null) { + mOnMediaRecorderListener.onRecordFailed(msg); + } + } + + /** + * 视频录制的监听 + */ + public interface OnMediaRecorderListener { + + /** + * 取消视频录制 + */ + void onCancel(); + + /** + * 视频录制成功 + * + * @param mediaObject 录制视频的信息 + */ + void onRecordSuccess(MediaObject mediaObject); + + /** + * 视频录制失败 + * + * @param msg 失败原因 + */ + void onRecordFailed(String msg); + + } + + +} diff --git a/xvideo-lib/src/main/java/com/xuexiang/xvideo/model/LocalMediaConfig.java b/xvideo-lib/src/main/java/com/xuexiang/xvideo/model/LocalMediaConfig.java index e00a7bd..85c497e 100644 --- a/xvideo-lib/src/main/java/com/xuexiang/xvideo/model/LocalMediaConfig.java +++ b/xvideo-lib/src/main/java/com/xuexiang/xvideo/model/LocalMediaConfig.java @@ -45,13 +45,13 @@ public final class LocalMediaConfig implements Parcelable { private final float scale; - private LocalMediaConfig(Buidler buidler) { - this.captureThumbnailsTime = buidler.captureThumbnailsTime; - this.FRAME_RATE = buidler.FRAME_RATE; - this.compressConfig = buidler.compressConfig; - this.videoAddress = buidler.videoPath; - this.scale = buidler.scale; - this.GO_HOME = buidler.GO_HOME; + private LocalMediaConfig(Builder builder) { + this.captureThumbnailsTime = builder.captureThumbnailsTime; + this.FRAME_RATE = builder.FRAME_RATE; + this.compressConfig = builder.compressConfig; + this.videoAddress = builder.videoPath; + this.scale = builder.scale; + this.GO_HOME = builder.GO_HOME; } @@ -118,7 +118,7 @@ public final class LocalMediaConfig implements Parcelable { } - public static class Buidler { + public static class Builder { /** * 录制后会剪切一帧缩略图并保存,就是取时间轴上这个时间的画面 */ @@ -141,7 +141,7 @@ public final class LocalMediaConfig implements Parcelable { * @param captureThumbnailsTime 会剪切一帧缩略图并保存,就是取时间轴上这个时间的画面 * @return */ - public Buidler captureThumbnailsTime(int captureThumbnailsTime) { + public Builder captureThumbnailsTime(int captureThumbnailsTime) { this.captureThumbnailsTime = captureThumbnailsTime; return this; } @@ -151,23 +151,23 @@ public final class LocalMediaConfig implements Parcelable { * {@link AutoVBRMode }{@link VBRMode}{@link CBRMode} * @return */ - public Buidler doH264Compress(BaseMediaBitrateConfig compressConfig) { + public Builder doH264Compress(BaseMediaBitrateConfig compressConfig) { this.compressConfig = compressConfig; return this; } - public Buidler goHome(boolean GO_HOME) { + public Builder goHome(boolean GO_HOME) { this.GO_HOME = GO_HOME; return this; } - public Buidler setFramerate(int MAX_FRAME_RATE) { + public Builder setFramerate(int MAX_FRAME_RATE) { this.FRAME_RATE = MAX_FRAME_RATE; return this; } - public Buidler setVideoPath(String videoPath) { + public Builder setVideoPath(String videoPath) { this.videoPath = videoPath; return this; } @@ -176,7 +176,7 @@ public final class LocalMediaConfig implements Parcelable { * @param scale 大于1,否者无效 * @return */ - public Buidler setScale(float scale) { + public Builder setScale(float scale) { if (scale <= 1) { this.scale = 1; } else { diff --git a/xvideo-lib/src/main/java/com/xuexiang/xvideo/model/MediaRecorderConfig.java b/xvideo-lib/src/main/java/com/xuexiang/xvideo/model/MediaRecorderConfig.java index 311605d..b274ff4 100644 --- a/xvideo-lib/src/main/java/com/xuexiang/xvideo/model/MediaRecorderConfig.java +++ b/xvideo-lib/src/main/java/com/xuexiang/xvideo/model/MediaRecorderConfig.java @@ -64,21 +64,22 @@ public final class MediaRecorderConfig implements Parcelable { private final boolean GO_HOME; - - private MediaRecorderConfig(Buidler buidler) { - this.FULL_SCREEN = buidler.FULL_SCREEN; - this.RECORD_TIME_MAX = buidler.RECORD_TIME_MAX; - this.RECORD_TIME_MIN = buidler.RECORD_TIME_MIN; - this.MAX_FRAME_RATE = buidler.MAX_FRAME_RATE; - this.captureThumbnailsTime = buidler.captureThumbnailsTime; - this.MIN_FRAME_RATE = buidler.MIN_FRAME_RATE; - this.SMALL_VIDEO_HEIGHT = buidler.SMALL_VIDEO_HEIGHT; - this.SMALL_VIDEO_WIDTH = buidler.SMALL_VIDEO_WIDTH; - this.VIDEO_BITRATE = buidler.VIDEO_BITRATE; - this.GO_HOME = buidler.GO_HOME; - + public static MediaRecorderConfig newInstance() { + return new Builder().build(); } + private MediaRecorderConfig(Builder builder) { + this.FULL_SCREEN = builder.fullscreen; + this.RECORD_TIME_MAX = builder.recordTimeMax; + this.RECORD_TIME_MIN = builder.recordTimeMin; + this.MAX_FRAME_RATE = builder.maxFrameRate; + this.captureThumbnailsTime = builder.captureThumbnailsTime; + this.MIN_FRAME_RATE = builder.minFrameRate; + this.SMALL_VIDEO_HEIGHT = builder.smallVideoHeight; + this.SMALL_VIDEO_WIDTH = builder.smallVideoWidth; + this.VIDEO_BITRATE = builder.videoBitrate; + this.GO_HOME = builder.goHome; + } protected MediaRecorderConfig(Parcel in) { FULL_SCREEN = in.readByte() != 0; @@ -105,7 +106,7 @@ public final class MediaRecorderConfig implements Parcelable { } }; - public boolean isGO_HOME() { + public boolean isGoHome() { return GO_HOME; } @@ -142,7 +143,6 @@ public final class MediaRecorderConfig implements Parcelable { return SMALL_VIDEO_WIDTH; } - public int getVideoBitrate() { return VIDEO_BITRATE; } @@ -166,47 +166,74 @@ public final class MediaRecorderConfig implements Parcelable { dest.writeByte((byte) (GO_HOME ? 1 : 0)); } + public static Builder newBuilder() { + return new Builder(); + } + + /** + * 默认录制最长时间 + */ + public static final int DEFAULT_RECORD_TIME_MAX = 6 * 1000; + /** + * 默认录制最短时间 + */ + public static final int DEFAULT_RECORD_TIME_MIN = (int) (1.5 * 1000); + /** + * 默认小视频高度 + */ + public static final int DEFAULT_SMALL_VIDEO_HEIGHT = 480; + /** + * 默认小视频宽度 + */ + public static final int DEFAULT_SMALL_VIDEO_WIDTH = 360; + /** + * 默认最大帧率 + */ + public static final int DEFAULT_SMALL_MAX_FRAME_RATE = 20; + /** + * 默认最小帧率 + */ + public static final int DEFAULT_SMALL_MIN_FRAME_RATE = 8; - public static class Buidler { + public static class Builder { /** * 录制时间 */ - private int RECORD_TIME_MAX = 6 * 1000; + private int recordTimeMax = DEFAULT_RECORD_TIME_MAX; + + private int recordTimeMin = DEFAULT_RECORD_TIME_MIN; /** * 小视频高度,TODO 注意:宽度不能随意穿,需要传送手机摄像头手支持录制的视频高度,注意是高度(因为会选择,具体原因不多解析)。 * 获取摄像头所支持的尺寸的方式是{@link android.graphics.Camera #getSupportedPreviewSizes()} * 一般支持的尺寸的高度有:240、480、720、1080等,具体值请用以上方法获取 */ - private int SMALL_VIDEO_HEIGHT = 480; + private int smallVideoHeight = DEFAULT_SMALL_VIDEO_HEIGHT; /** * 小视频宽度 */ - private int SMALL_VIDEO_WIDTH = 360; + private int smallVideoWidth = DEFAULT_SMALL_VIDEO_WIDTH; /** * 最大帧率 */ - private int MAX_FRAME_RATE = 20; + private int maxFrameRate = DEFAULT_SMALL_MAX_FRAME_RATE; /** * 最小帧率 */ - private int MIN_FRAME_RATE = 8; + private int minFrameRate = DEFAULT_SMALL_MIN_FRAME_RATE; /** * 视频码率//todo 注意传入>0的值后码率模式将从VBR变成CBR */ - private int VIDEO_BITRATE; + private int videoBitrate; /** * 录制后会剪切一帧缩略图并保存,就是取时间轴上这个时间的画面 */ private int captureThumbnailsTime = 1; + private boolean goHome = false; - private boolean GO_HOME = false; - - public int RECORD_TIME_MIN = (int) (1.5 * 1000); - - private boolean FULL_SCREEN = false; + private boolean fullscreen = false; public MediaRecorderConfig build() { @@ -217,18 +244,17 @@ public final class MediaRecorderConfig implements Parcelable { * @param captureThumbnailsTime 录制后会剪切一帧缩略图并保存,就是取时间轴上这个时间的画面 * @return */ - public Buidler captureThumbnailsTime(int captureThumbnailsTime) { + public Builder captureThumbnailsTime(int captureThumbnailsTime) { this.captureThumbnailsTime = captureThumbnailsTime; return this; } - /** * @param MAX_FRAME_RATE 最大帧率(与视频清晰度、大小息息相关) * @return */ - public Buidler maxFrameRate(int MAX_FRAME_RATE) { - this.MAX_FRAME_RATE = MAX_FRAME_RATE; + public Builder maxFrameRate(int MAX_FRAME_RATE) { + this.maxFrameRate = MAX_FRAME_RATE; return this; } @@ -236,8 +262,8 @@ public final class MediaRecorderConfig implements Parcelable { * @param MIN_FRAME_RATE 最小帧率(与视频清晰度、大小息息相关) * @return */ - public Buidler minFrameRate(int MIN_FRAME_RATE) { - this.MIN_FRAME_RATE = MIN_FRAME_RATE; + public Builder minFrameRate(int MIN_FRAME_RATE) { + this.minFrameRate = MIN_FRAME_RATE; return this; } @@ -245,8 +271,8 @@ public final class MediaRecorderConfig implements Parcelable { * @param RECORD_TIME_MAX 录制时间 * @return */ - public Buidler recordTimeMax(int RECORD_TIME_MAX) { - this.RECORD_TIME_MAX = RECORD_TIME_MAX; + public Builder recordTimeMax(int RECORD_TIME_MAX) { + this.recordTimeMax = RECORD_TIME_MAX; return this; } @@ -254,8 +280,8 @@ public final class MediaRecorderConfig implements Parcelable { * @param RECORD_TIME_MIN 最少录制时间 * @return */ - public Buidler recordTimeMin(int RECORD_TIME_MIN) { - this.RECORD_TIME_MIN = RECORD_TIME_MIN; + public Builder recordTimeMin(int RECORD_TIME_MIN) { + this.recordTimeMin = RECORD_TIME_MIN; return this; } @@ -266,8 +292,8 @@ public final class MediaRecorderConfig implements Parcelable { * 一般支持的尺寸的高度有:240、480、720、1080等,具体值请用以上方法获取 * @return */ - public Buidler smallVideoHeight(int SMALL_VIDEO_HEIGHT) { - this.SMALL_VIDEO_HEIGHT = SMALL_VIDEO_HEIGHT; + public Builder smallVideoHeight(int SMALL_VIDEO_HEIGHT) { + this.smallVideoHeight = SMALL_VIDEO_HEIGHT; return this; } @@ -275,8 +301,8 @@ public final class MediaRecorderConfig implements Parcelable { * @param SMALL_VIDEO_WIDTH * @return */ - public Buidler smallVideoWidth(int SMALL_VIDEO_WIDTH) { - this.SMALL_VIDEO_WIDTH = SMALL_VIDEO_WIDTH; + public Builder smallVideoWidth(int SMALL_VIDEO_WIDTH) { + this.smallVideoWidth = SMALL_VIDEO_WIDTH; return this; } @@ -284,18 +310,18 @@ public final class MediaRecorderConfig implements Parcelable { * @param VIDEO_BITRATE 视频码率 * @return */ - public Buidler videoBitrate(int VIDEO_BITRATE) { - this.VIDEO_BITRATE = VIDEO_BITRATE; + public Builder videoBitrate(int VIDEO_BITRATE) { + this.videoBitrate = VIDEO_BITRATE; return this; } - public Buidler goHome(boolean GO_HOME) { - this.GO_HOME = GO_HOME; + public Builder goHome(boolean GO_HOME) { + this.goHome = GO_HOME; return this; } - public Buidler fullScreen(boolean full) { - this.FULL_SCREEN = full; + public Builder fullScreen(boolean full) { + this.fullscreen = full; return this; } } diff --git a/xvideo-lib/src/main/res/layout/xvideo_activity_media_recorder.xml b/xvideo-lib/src/main/res/layout/xvideo_activity_media_recorder.xml index fe158a3..7c13e19 100644 --- a/xvideo-lib/src/main/res/layout/xvideo_activity_media_recorder.xml +++ b/xvideo-lib/src/main/res/layout/xvideo_activity_media_recorder.xml @@ -1,122 +1,12 @@ - - - + + android:orientation="vertical"> - - - - - - - - - - - - - - - - - - - - - - - - - - - - + android:layout_height="match_parent" /> - \ No newline at end of file + \ No newline at end of file diff --git a/xvideo-lib/src/main/res/layout/xvideo_layout_media_recorder.xml b/xvideo-lib/src/main/res/layout/xvideo_layout_media_recorder.xml new file mode 100644 index 0000000..fe158a3 --- /dev/null +++ b/xvideo-lib/src/main/res/layout/xvideo_layout_media_recorder.xml @@ -0,0 +1,122 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file -- GitLab