边录像边拍照 ,Android怎样实现录制视频时拍照(或连续取录制的实时这一帧),就是同时录像时可以连续保存图片到本地,录像也同时保存
/**
* @author: Jeff <15899859876@qq.com>
* @date: 2018-03-05 15:08
* @description: PLRecordStateListener注册录制状态监听器 , PLVideoSaveListener注册保存视频的监听器
*/
public class VideoActivity extends AppCompatActivity implements PLRecordStateListener, PLVideoSaveListener {
//captureIV 拍照,deleteIV 删除视频,recordIV 录像,concatIV 保存
Button captureIV, deleteIV, recordIV, concatIV;
private void setClickListener() {
recordIV.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// newFilepath(true);
// 开始一段视频
mShortVideoRecorder.beginSection();
newFilepath(true);
}
});
deleteIV.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 回删上一段视频
mShortVideoRecorder.deleteLastSection();
}
});
concatIV.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//先结束视频再保存
mShortVideoRecorder.endSection();
// 合成和保存所有的视频片段
mShortVideoRecorder.concatSections(VideoActivity.this);
}
});
captureIV.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//拍照
newFilepath(false);
}
});
}
String VIDEO_STORAGE_DIR = Environment.getExternalStorageDirectory() + "/ShortVideo/";
String VIDEO_FILE_PATH = "";
String JPEG_FILE_PATH = "";
private void newFilepath(boolean path) {
//创建一个地址,保存图片和视频为时间戳地址
if (path) {//视频
//如果是true那就设置 视频
VIDEO_FILE_PATH = VIDEO_STORAGE_DIR + "video" + System.currentTimeMillis() + ".mp4";
mRecordSetting.setVideoFilepath(VIDEO_FILE_PATH);
} else {
//如果是false那就设置 图片
JPEG_FILE_PATH = VIDEO_STORAGE_DIR + "img" + System.currentTimeMillis() + ".jpg";
onCaptureFrame();
}
}
public void onCaptureFrame() {
//获得视频帧,并做保存
mShortVideoRecorder.captureFrame(new PLCaptureFrameListener() {
@Override
public void onFrameCaptured(PLVideoFrame capturedFrame) {
if (capturedFrame == null) {
Log.d("VideoActivity", "capture frame failed");
return;
}
//获取帧数据 byte[] getData()
//将帧数据转换为 Bitmap toBitmap();
// Bitmap bitmap = capturedFrame.toBitmap();
FileOutputStream fos = null;
try {
fos = new FileOutputStream(JPEG_FILE_PATH);
capturedFrame.toBitmap().compress(Bitmap.CompressFormat.JPEG, 100, fos);
fos.flush();
fos.close();
// runOnUiThread(new Runnable() {
// @Override
// public void run() {
// // ToastUtils.s(VideoRecordActivity.this, "截帧已保存到路径:" + Config.CAPTURED_FRAME_FILE_PATH);
// }
// });
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fos != null) {
try {
fos.flush();
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
});
}
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//设置屏幕常亮
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
setContentView(R.layout.activity_video_record);
//captureIV 拍照,deleteIV 删除视频,recordIV 录像,concatIV 保存
captureIV = (Button) findViewById(R.id.capture_frame_button);
recordIV = (Button) findViewById(R.id.record);
deleteIV = (Button) findViewById(R.id.delete);
concatIV = (Button) findViewById(R.id.concat);
glSurfaceView = (GLSurfaceView) findViewById(R.id.preview);
intioVideo();
setClickListener();
}
//拍摄视频需要的预览窗口为 GLSurfaceView
GLSurfaceView glSurfaceView;
//创建录制对象
PLShortVideoRecorder mShortVideoRecorder;
//摄像头采集选项
PLCameraSetting mCameraSetting;
// 麦克风采集选项
PLMicrophoneSetting mMicrophoneSetting;
// 视频编码选项
PLVideoEncodeSetting mVideoEncodeSetting;
音频编码选项
PLAudioEncodeSetting mAudioEncodeSetting;
// 美颜选项
PLFaceBeautySetting mFaceBeautySetting;
// 录制选项 //配置拍摄参数 缓存目录,拍摄时长等
PLRecordSetting mRecordSetting;
//视频对象初始化
private void intioVideo() {
// 创建录制对象
mShortVideoRecorder = new PLShortVideoRecorder();
mShortVideoRecorder.setRecordStateListener(this);
// mShortVideoRecorder.setPLVideoSaveListener()
//mShortVideoRecorder.setVideoFilterListener();
// 摄像头采集选项
mCameraSetting = new PLCameraSetting();
mCameraSetting.setCameraId(chooseCameraFacingId());
mCameraSetting.setCameraPreviewSizeRatio(PLCameraSetting.CAMERA_PREVIEW_SIZE_RATIO.RATIO_4_3);
mCameraSetting.setCameraPreviewSizeLevel(PLCameraSetting.CAMERA_PREVIEW_SIZE_LEVEL.PREVIEW_SIZE_LEVEL_1080P);
// 麦克风采集选项
mMicrophoneSetting = new PLMicrophoneSetting();
// AudioFormat.CHANNEL_IN_MONO 16 : AudioFormat.CHANNEL_IN_STEREO 12
mMicrophoneSetting.setChannelConfig(AudioFormat.CHANNEL_IN_MONO);
// 视频编码选项
mVideoEncodeSetting = new PLVideoEncodeSetting(this);
mVideoEncodeSetting.setEncodingSizeLevel(PLVideoEncodeSetting.VIDEO_ENCODING_SIZE_LEVEL.VIDEO_ENCODING_SIZE_LEVEL_480P_1); // 480x480
mVideoEncodeSetting.setEncodingBitrate(1000 * 1000); // 1000kbps
mVideoEncodeSetting.setEncodingFps(24);
mVideoEncodeSetting.setHWCodecEnabled(true); // true:硬编 false:软编
// 音频编码选项
mAudioEncodeSetting = new PLAudioEncodeSetting();
mAudioEncodeSetting.setHWCodecEnabled(true); // true:硬编 false:软编
mAudioEncodeSetting.setChannels(1);
// mAudioEncodeSetting.setChannels(RecordSettings.AUDIO_CHANNEL_NUM_ARRAY[audioChannelNumPos]);
// 美颜选项
mFaceBeautySetting = new PLFaceBeautySetting(1.0f, 0.5f, 0.5f);
//配置拍摄参数 缓存目录,拍摄时长等
mRecordSetting = new PLRecordSetting();
60s 慢 mRecordSpeed = RECORD_SPEED_ARRAY[1]; 正常 mRecordSpeed = RECORD_SPEED_ARRAY[2];
mRecordSetting.setMaxRecordDuration((long) (10 * 1000 * 6 * 1));
mRecordSetting.setVideoCacheDir(VIDEO_STORAGE_DIR);
// mRecordSetting.setVideoFilepath(VIDEO_FILE_PATH);
// //通过 prepare 把拍摄所选择的参数配置到 SDK 中
// mShortVideoRecorder.prepare(glSurfaceView, mCameraSetting, mMicrophoneSetting,
// mVideoEncodeSetting, mAudioEncodeSetting, mFaceBeautySetting, mRecordSetting);
newFilepath(true);
//通过 prepare 把拍摄所选择的参数配置到 SDK 中
mShortVideoRecorder.prepare(glSurfaceView, mCameraSetting, mMicrophoneSetting,
mVideoEncodeSetting, mAudioEncodeSetting, mFaceBeautySetting, mRecordSetting);
}
private PLCameraSetting.CAMERA_FACING_ID chooseCameraFacingId() {
if (PLCameraSetting.hasCameraFacing(PLCameraSetting.CAMERA_FACING_ID.CAMERA_FACING_3RD)) {
return PLCameraSetting.CAMERA_FACING_ID.CAMERA_FACING_3RD;
} else if (PLCameraSetting.hasCameraFacing(PLCameraSetting.CAMERA_FACING_ID.CAMERA_FACING_FRONT)) {
return PLCameraSetting.CAMERA_FACING_ID.CAMERA_FACING_FRONT;
} else {
return PLCameraSetting.CAMERA_FACING_ID.CAMERA_FACING_BACK;
}
}
//在 Activity 的各个生命周期的回调中,分别调用 PLShortVideoRecorder 对应的方法*************
@Override
protected void onResume() {
super.onResume();
mShortVideoRecorder.resume();
}
@Override
protected void onPause() {
super.onPause();
mShortVideoRecorder.pause();
}
@Override
protected void onDestroy() {
super.onDestroy();
mShortVideoRecorder.destroy();
}
//PLRecordStateListener注册录制状态监听器********************************************
@Override
public void onReady() {
// 当准备完毕可以进行录制时触发
// ToastUtils.s(VideoActivity.this, "可以开始拍摄咯");
Log.d("VideoActivity", "onReady()");
}
@Override
public void onError(int i) {
//当有异常发生时触发\
Log.d("VideoActivity", "onError(int i)---= " + i);
}
@Override
public void onDurationTooShort() {
//当录制的片段过短时触发
Log.d("VideoActivity", "onDurationTooShort()");
}
@Override
public void onRecordStarted() {
//录制开始
Log.d("VideoActivity", "onRecordStarted() ");
}
@Override
public void onRecordStopped() {
//录制结束
Log.d("VideoActivity", "onRecordStopped()");
}
@Override
public void onSectionIncreased(long incDuration, long totalDuration, int sectionCount) {
/**
* 当有新片段录制完成时触发
* @param incDuration 增加的时长
* @param totalDuration 总时长
* @param sectionCount 当前的片段总数
*/
Log.d("VideoActivity",
"onSectionIncreased(long incDuration, long totalDuration, int sectionCount)==incDuration"
+ incDuration + "---totalDuration=" + totalDuration + "----sectionCount=" + sectionCount
);
}
@Override
public void onSectionDecreased(long decDuration, long totalDuration, int sectionCount) {
/**
* 当有片段被删除时触发
* @param decDuration 减少的时长
* @param totalDuration 总时长
* @param sectionCount 当前的片段总数
*/
Log.d("VideoActivity",
"onSectionDecreased(long decDuration, long totalDuration, int sectionCount)==sectionCount="
+ decDuration + "---totalDuration=" + totalDuration + "---sectionCount=" + sectionCount);
}
@Override
public void onRecordCompleted() {
//当录制全部完成时触发
Log.d("VideoActivity", "onRecordCompleted()");
}
//**************PLVideoSaveListener注册保存视频的监听器***************************************
@Override
public void onSaveVideoSuccess(String s) {
//保存成功时触发
//在这里可以上传文件
Log.d("VideoActivity", " onSaveVideoSuccess(String s)===" + s);
//compressVideoResouce(s);
}
@Override
public void onSaveVideoFailed(int i) {
//保存失败时触发
Log.d("VideoActivity", "nSaveVideoFailed(int i)==" + i);
}
@Override
public void onSaveVideoCanceled() {
//保存取消时触发
Log.d("VideoActivity", "onSaveVideoCanceled()");
}
@Override
public void onProgressUpdate(float v) {
//保存进度更新时触发
Log.d("VideoActivity", "onProgressUpdate(float v)==" + v);
}
/**
* 压缩视频
*
* @param filepath
*/
public void compressVideoResouce(String filepath) {
if (TextUtils.isEmpty(filepath)) {
//ToastUtils.s(VideoActivity.this, "请先选择转码文件!");
return;
}
//PLShortVideoTranscoder初始化,三个参数,第一个context,第二个要压缩文件的路径,第三个视频压缩后输出的路径
// PLShortVideoTranscoder mShortVideoTranscoder = new PLShortVideoTranscoder(this, filepath,
// APPConstant.LOCAL_PATH + "compress/transcoded.mp4");
PLShortVideoTranscoder mShortVideoTranscoder = new PLShortVideoTranscoder(this, filepath,
VIDEO_STORAGE_DIR + "newVideo" + System.currentTimeMillis() + ".mp4");
MediaMetadataRetriever retr = new MediaMetadataRetriever();
retr.setDataSource(filepath);
String height = retr.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT); // 视频高度
String width = retr.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH); // 视频宽度
//String rotation = retr.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_ROTATION); // 视频旋转方向
int transcodingBitrateLevel = 6;//我这里选择的2500*1000压缩,这里可以自己选择合适的压缩比例
mShortVideoTranscoder.transcode(Integer.parseInt(width), Integer.parseInt(height), getEncodingBitrateLevel(transcodingBitrateLevel),
false, new PLVideoSaveListener() {
@Override
public void onSaveVideoSuccess(String s) {
//保存成功时触发
//在这里可以上传文件
Log.d("VideoActivity", " onSaveVideoSuccess(String s)===" + s);
}
@Override
public void onSaveVideoFailed(final int errorCode) {
runOnUiThread(new Runnable() {
@Override
public void run() {
switch (errorCode) {
case PLErrorCode.ERROR_NO_VIDEO_TRACK:
// ToastUtils.s(VideoActivity.this, "该文件没有视频信息!");
break;
case PLErrorCode.ERROR_SRC_DST_SAME_FILE_PATH:
// ToastUtils.s(VideoActivity.this, "源文件路径和目标路径不能相同!");
break;
case PLErrorCode.ERROR_LOW_MEMORY:
// ToastUtils.s(VideoActivity.this, "手机内存不足,无法对该视频进行时光倒流!");
break;
default:
// ToastUtils.s(VideoActivity.this, "transcode failed: " + errorCode);
}
}
});
}
@Override
public void onSaveVideoCanceled() {
// LogUtil.e("onSaveVideoCanceled");
}
@Override
public void onProgressUpdate(float percentage) {
// LogUtil.e("onProgressUpdate==========" + percentage);
}
});
}
/**
* 设置压缩质量
*
* @param position
* @return
*/
private int getEncodingBitrateLevel(int position) {
return ENCODING_BITRATE_LEVEL_ARRAY[position];
}
/**
* 选的越高文件质量越大,质量越好
*/
public static final int[] ENCODING_BITRATE_LEVEL_ARRAY = {
500 * 1000,
800 * 1000,
1000 * 1000,
1200 * 1000,
1600 * 1000,
2000 * 1000,
2500 * 1000,
4000 * 1000,
8000 * 1000,
};
}
原文:https://blog.csdn.net/Jeff_YaoJie/article/details/79460323