init
This commit is contained in:
commit
e2dc633112
21
package.json
Normal file
21
package.json
Normal file
@ -0,0 +1,21 @@
|
||||
{
|
||||
"name": "capture-cordova-plugin",
|
||||
"version": "1.0.0",
|
||||
"description": "Video or Photo Capture Cordova Plugin",
|
||||
"cordova": {
|
||||
"id": "capture-cordova-plugin",
|
||||
"platforms": [
|
||||
"android"
|
||||
]
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://m.shuto.cn:8680/center/capture-cordova-plugin.git"
|
||||
},
|
||||
"keywords": [
|
||||
"ecosystem:cordova",
|
||||
"cordova-android"
|
||||
],
|
||||
"author": "shuto",
|
||||
"license": "ISC"
|
||||
}
|
35
plugin.xml
Normal file
35
plugin.xml
Normal file
@ -0,0 +1,35 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<plugin id="capture-cordova-plugin" version="1.0.0"
|
||||
xmlns="http://apache.org/cordova/ns/plugins/1.0"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<name>Capture Plugin</name>
|
||||
|
||||
<js-module name="capture-cordova-plugin" src="www/capture.js">
|
||||
<clobbers target="capture" />
|
||||
</js-module>
|
||||
|
||||
<platform name="android">
|
||||
<config-file parent="/*" target="res/xml/config.xml">
|
||||
<feature name="capture-cordova-plugin">
|
||||
<param name="android-package" value="cn.shuto.plugin.capture.CaptureCordovaPlugin" />
|
||||
</feature>
|
||||
</config-file>
|
||||
<config-file target="AndroidManifest.xml" parent="/*">
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
</config-file>
|
||||
<config-file target="AndroidManifest.xml" parent="application">
|
||||
<activity android:name="com.mabeijianxi.smallvideorecord2.MediaRecorderActivity" android:clearTaskOnLaunch="true" android:configChanges="orientation|keyboardHidden|screenSize" android:theme="@android:style/Theme.NoTitleBar.Fullscreen" android:windowSoftInputMode="stateAlwaysHidden" android:exported="false"/>
|
||||
<provider android:name="org.apache.cordova.camera.FileProvider" android:authorities="${applicationId}.cordova.plugin.camera.provider" android:exported="false" android:grantUriPermissions="true">
|
||||
<meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/camera_provider_paths"/>
|
||||
</provider>
|
||||
</config-file>
|
||||
<config-file target="AndroidManifest.xml" parent="/manifest">
|
||||
<uses-permission android:name="android.permission.CAMERA"/>
|
||||
<uses-permission android:name="android.permission.FLASHLIGHT"/>
|
||||
<uses-feature android:name="android.hardware.camera" android:required="true"/>
|
||||
</config-file>
|
||||
|
||||
<source-file src="src/android/CaptureCordovaPlugin.java" target-dir="src/cn/shuto/plugin/capture" />
|
||||
<framework custom="true" src="src/android/mobile-ffmpeg-x2.gradle" type="gradleReference" />
|
||||
</platform>
|
||||
</plugin>
|
31
src/android/CaptureCordovaPlugin.java
Normal file
31
src/android/CaptureCordovaPlugin.java
Normal file
@ -0,0 +1,31 @@
|
||||
package cn.shuto.plugin.capture;
|
||||
|
||||
import org.apache.cordova.CordovaPlugin;
|
||||
import org.apache.cordova.CallbackContext;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
/**
|
||||
* This class echoes a string called from JavaScript.
|
||||
*/
|
||||
public class CaptureCordovaPlugin extends CordovaPlugin {
|
||||
public static final int REQUEST_CODE = 0x777578;
|
||||
private CallbackContext callbackContext;
|
||||
|
||||
@Override
|
||||
public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
|
||||
this.callbackContext = callbackContext;
|
||||
JSONObject param = args.optJSONObject(0);
|
||||
if (action.equals("capture")) {
|
||||
this.capture(param, callbackContext);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void capture(JSONObject param, CallbackContext callbackContext) {
|
||||
}
|
||||
|
||||
}
|
704
src/android/MediaRecorderActivity.java
Normal file
704
src/android/MediaRecorderActivity.java
Normal file
@ -0,0 +1,704 @@
|
||||
package com.mabeijianxi.smallvideorecord2;
|
||||
|
||||
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.widget.Toast;
|
||||
|
||||
import com.mabeijianxi.smallvideorecord2.model.MediaObject;
|
||||
import com.mabeijianxi.smallvideorecord2.model.MediaRecorderConfig;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import static com.mabeijianxi.smallvideorecord2.R.id.bottom_layout;
|
||||
|
||||
|
||||
/**
|
||||
* 视频录制
|
||||
*/
|
||||
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;
|
||||
/**
|
||||
* 回删按钮、延时按钮、滤镜按钮
|
||||
*/
|
||||
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;
|
||||
/**
|
||||
* 是否已经释放
|
||||
*/
|
||||
private volatile boolean mReleased;
|
||||
/**
|
||||
* 视屏地址
|
||||
*/
|
||||
public final static String VIDEO_URI = "video_uri";
|
||||
/**
|
||||
* 本次视频保存的文件夹地址
|
||||
*/
|
||||
public final static String OUTPUT_DIRECTORY = "output_directory";
|
||||
/**
|
||||
* 视屏截图地址
|
||||
*/
|
||||
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;
|
||||
|
||||
/**
|
||||
* @param context
|
||||
* @param overGOActivityName 录制结束后需要跳转的Activity全类名
|
||||
*/
|
||||
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.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(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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化画布
|
||||
*/
|
||||
private void initSurfaceView() {
|
||||
if (NEED_FULL_SCREEN) {
|
||||
mBottomLayout.setBackgroundColor(0);
|
||||
title_layout.setBackgroundColor(getResources().getColor(R.color.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.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
|
||||
*/
|
||||
private void initMediaRecorder() {
|
||||
mMediaRecorder = new MediaRecorderNative();
|
||||
|
||||
mMediaRecorder.setOnErrorListener(this);
|
||||
mMediaRecorder.setOnEncodeListener(this);
|
||||
mMediaRecorder.setOnPreparedListener(this);
|
||||
|
||||
File f = new File(JianXiCamera.getVideoCachePath());
|
||||
if (!FileUtils.checkFile(f)) {
|
||||
f.mkdirs();
|
||||
}
|
||||
String key = String.valueOf(System.currentTimeMillis());
|
||||
mMediaObject = mMediaRecorder.setOutputDirectory(key,
|
||||
JianXiCamera.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() >= 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) {
|
||||
|
||||
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,
|
||||
RECORD_TIME_MAX - mMediaObject.getDuration());
|
||||
}
|
||||
// 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.hint)
|
||||
.setMessage(R.string.record_camera_exit_dialog_message)
|
||||
.setNegativeButton(
|
||||
R.string.record_camera_cancel_dialog_yes,
|
||||
new DialogInterface.OnClickListener() {
|
||||
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog,
|
||||
int which) {
|
||||
mMediaObject.delete();
|
||||
finish();
|
||||
}
|
||||
|
||||
})
|
||||
.setPositiveButton(R.string.record_camera_cancel_dialog_no,
|
||||
null).setCancelable(false).show();
|
||||
return;
|
||||
}
|
||||
|
||||
if (mMediaObject != null)
|
||||
mMediaObject.delete();
|
||||
finish();
|
||||
}
|
||||
|
||||
/**
|
||||
* 停止录制
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public void onEncodeStart() {
|
||||
showProgress("", getString(R.string.record_camera_progress_message));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onEncodeProgress(int progress) {
|
||||
}
|
||||
|
||||
/**
|
||||
* 转码完成
|
||||
*/
|
||||
@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);
|
||||
startActivity(intent);
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new IllegalArgumentException("需要传入录制完成后跳转的Activity的全类名");
|
||||
}
|
||||
|
||||
finish();
|
||||
}
|
||||
|
||||
/**
|
||||
* 转码失败 检查sdcard是否可用,检查分块是否存在
|
||||
*/
|
||||
@Override
|
||||
public void onEncodeError() {
|
||||
hideProgress();
|
||||
Toast.makeText(this, R.string.record_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() {
|
||||
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;
|
||||
|
||||
}
|
||||
|
||||
}
|
285
src/android/MediaRecorderConfig.java
Normal file
285
src/android/MediaRecorderConfig.java
Normal file
@ -0,0 +1,285 @@
|
||||
package com.mabeijianxi.smallvideorecord2.model;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
/**
|
||||
* Created by jian on 2016/8/25 17:03
|
||||
* https://github.com/mabeijianxi
|
||||
* mabeijianxi@gmail.com
|
||||
*/
|
||||
public final class MediaRecorderConfig implements Parcelable {
|
||||
|
||||
private final boolean FULL_SCREEN;
|
||||
/**
|
||||
* 录制时间
|
||||
*/
|
||||
private final int RECORD_TIME_MAX;
|
||||
/**
|
||||
* 最少录制时间
|
||||
*/
|
||||
private final int RECORD_TIME_MIN;
|
||||
|
||||
/**
|
||||
* 小视频高度
|
||||
* <p>注意:宽度不能随意传,需要传送手机摄像头手支持录制的视频高度,注意是高度(因为会选择,具体原因不多解释)。
|
||||
* 获取摄像头所支持的尺寸的方式是{@link android.graphics.Camera#getSupportedPreviewSizes()}
|
||||
* 一般支持的尺寸的高度有:240、480、720、1080等,具体值请用以上方法获取</p>
|
||||
*/
|
||||
private final int SMALL_VIDEO_HEIGHT;
|
||||
/**
|
||||
* 小视频宽度
|
||||
*/
|
||||
private final int SMALL_VIDEO_WIDTH;
|
||||
/**
|
||||
* 最大帧率
|
||||
*/
|
||||
private final int MAX_FRAME_RATE;
|
||||
/**
|
||||
* 最小帧率
|
||||
*/
|
||||
private final int MIN_FRAME_RATE;
|
||||
/**
|
||||
* 视频码率
|
||||
*/
|
||||
private final int VIDEO_BITRATE;
|
||||
/**
|
||||
* 录制后会剪切一帧缩略图并保存,就是取时间轴上这个时间的画面
|
||||
*/
|
||||
private final int captureThumbnailsTime;
|
||||
|
||||
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;
|
||||
|
||||
}
|
||||
|
||||
protected MediaRecorderConfig(Parcel in) {
|
||||
FULL_SCREEN = in.readByte() != 0;
|
||||
RECORD_TIME_MAX = in.readInt();
|
||||
RECORD_TIME_MIN = in.readInt();
|
||||
SMALL_VIDEO_HEIGHT = in.readInt();
|
||||
SMALL_VIDEO_WIDTH = in.readInt();
|
||||
MAX_FRAME_RATE = in.readInt();
|
||||
MIN_FRAME_RATE = in.readInt();
|
||||
VIDEO_BITRATE = in.readInt();
|
||||
captureThumbnailsTime = in.readInt();
|
||||
GO_HOME = in.readByte() != 0;
|
||||
}
|
||||
|
||||
public static final Creator<MediaRecorderConfig> CREATOR = new Creator<MediaRecorderConfig>() {
|
||||
@Override
|
||||
public MediaRecorderConfig createFromParcel(Parcel in) {
|
||||
return new MediaRecorderConfig(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MediaRecorderConfig[] newArray(int size) {
|
||||
return new MediaRecorderConfig[size];
|
||||
}
|
||||
};
|
||||
|
||||
public boolean isGO_HOME() {
|
||||
return GO_HOME;
|
||||
}
|
||||
public boolean getFullScreen() {
|
||||
return FULL_SCREEN;
|
||||
}
|
||||
public int getCaptureThumbnailsTime() {
|
||||
return captureThumbnailsTime;
|
||||
}
|
||||
|
||||
|
||||
public int getMaxFrameRate() {
|
||||
return MAX_FRAME_RATE;
|
||||
}
|
||||
|
||||
public int getMinFrameRate() {
|
||||
return MIN_FRAME_RATE;
|
||||
}
|
||||
|
||||
public int getRecordTimeMax() {
|
||||
return RECORD_TIME_MAX;
|
||||
}
|
||||
public int getRecordTimeMin() {
|
||||
return RECORD_TIME_MIN;
|
||||
}
|
||||
|
||||
public int getSmallVideoHeight() {
|
||||
return SMALL_VIDEO_HEIGHT;
|
||||
}
|
||||
|
||||
public int getSmallVideoWidth() {
|
||||
return SMALL_VIDEO_WIDTH;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public int getVideoBitrate() {
|
||||
return VIDEO_BITRATE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
dest.writeByte((byte) (FULL_SCREEN ? 1 : 0));
|
||||
dest.writeInt(RECORD_TIME_MAX);
|
||||
dest.writeInt(RECORD_TIME_MIN);
|
||||
dest.writeInt(SMALL_VIDEO_HEIGHT);
|
||||
dest.writeInt(SMALL_VIDEO_WIDTH);
|
||||
dest.writeInt(MAX_FRAME_RATE);
|
||||
dest.writeInt(MIN_FRAME_RATE);
|
||||
dest.writeInt(VIDEO_BITRATE);
|
||||
dest.writeInt(captureThumbnailsTime);
|
||||
dest.writeByte((byte) (GO_HOME ? 1 : 0));
|
||||
}
|
||||
|
||||
|
||||
public static class Buidler {
|
||||
/**
|
||||
* 录制时间
|
||||
*/
|
||||
private int RECORD_TIME_MAX = 6 * 1000;
|
||||
|
||||
/**
|
||||
* 小视频高度
|
||||
* <p>注意:宽度不能随意传,需要传送手机摄像头手支持录制的视频高度,注意是高度(因为会选择,具体原因不多解释)。
|
||||
* 获取摄像头所支持的尺寸的方式是{@link android.graphics.Camera#getSupportedPreviewSizes()}
|
||||
* 一般支持的尺寸的高度有:240、480、720、1080等,具体值请用以上方法获取</p>
|
||||
*/
|
||||
private int SMALL_VIDEO_HEIGHT = 480;
|
||||
|
||||
/**
|
||||
* 小视频宽度
|
||||
*/
|
||||
private int SMALL_VIDEO_WIDTH = 360;
|
||||
/**
|
||||
* 最大帧率
|
||||
*/
|
||||
private int MAX_FRAME_RATE = 20;
|
||||
/**
|
||||
* 最小帧率
|
||||
*/
|
||||
private int MIN_FRAME_RATE = 8;
|
||||
/**
|
||||
* 视频码率//todo 注意传入>0的值后码率模式将从VBR变成CBR
|
||||
*/
|
||||
private int VIDEO_BITRATE;
|
||||
/**
|
||||
* 录制后会剪切一帧缩略图并保存,就是取时间轴上这个时间的画面
|
||||
*/
|
||||
private int captureThumbnailsTime = 1;
|
||||
|
||||
|
||||
private boolean GO_HOME=false;
|
||||
|
||||
public int RECORD_TIME_MIN= (int) (1.5*1000);
|
||||
|
||||
private boolean FULL_SCREEN =false;
|
||||
|
||||
|
||||
public MediaRecorderConfig build() {
|
||||
return new MediaRecorderConfig(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param captureThumbnailsTime 录制后会剪切一帧缩略图并保存,就是取时间轴上这个时间的画面
|
||||
* @return
|
||||
*/
|
||||
public Buidler 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;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param MIN_FRAME_RATE 最小帧率(与视频清晰度、大小息息相关)
|
||||
* @return
|
||||
*/
|
||||
public Buidler minFrameRate(int MIN_FRAME_RATE) {
|
||||
this.MIN_FRAME_RATE = MIN_FRAME_RATE;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param RECORD_TIME_MAX 录制时间
|
||||
* @return
|
||||
*/
|
||||
public Buidler recordTimeMax(int RECORD_TIME_MAX) {
|
||||
this.RECORD_TIME_MAX = RECORD_TIME_MAX;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param RECORD_TIME_MIN 最少录制时间
|
||||
* @return
|
||||
*/
|
||||
public Buidler recordTimeMin(int RECORD_TIME_MIN) {
|
||||
this.RECORD_TIME_MIN = RECORD_TIME_MIN;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @param SMALL_VIDEO_HEIGHT
|
||||
* @return
|
||||
*/
|
||||
public Buidler smallVideoHeight(int SMALL_VIDEO_HEIGHT) {
|
||||
this.SMALL_VIDEO_HEIGHT = SMALL_VIDEO_HEIGHT;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param SMALL_VIDEO_WIDTH
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public Buidler smallVideoWidth(int SMALL_VIDEO_WIDTH) {
|
||||
this.SMALL_VIDEO_WIDTH = SMALL_VIDEO_WIDTH;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param VIDEO_BITRATE 视频码率
|
||||
* @return
|
||||
*/
|
||||
public Buidler videoBitrate(int VIDEO_BITRATE) {
|
||||
this.VIDEO_BITRATE = VIDEO_BITRATE;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Buidler goHome(boolean GO_HOME) {
|
||||
this.GO_HOME = GO_HOME;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Buidler fullScreen(boolean full) {
|
||||
this.FULL_SCREEN =full;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
3
src/android/mobile-ffmpeg-x2.gradle
Normal file
3
src/android/mobile-ffmpeg-x2.gradle
Normal file
@ -0,0 +1,3 @@
|
||||
dependencies {
|
||||
implementation 'com.arthenica:mobile-ffmpeg-full:4.4'
|
||||
}
|
7
www/capture.js
Normal file
7
www/capture.js
Normal file
@ -0,0 +1,7 @@
|
||||
var exec = require('cordova/exec');
|
||||
|
||||
module.exports = {
|
||||
capture(option, success, error) {
|
||||
exec(success, error, 'capture-cordova-plugin', 'capture', [option]);
|
||||
}
|
||||
};
|
Loading…
x
Reference in New Issue
Block a user