這篇文章主要介紹Android5.0以上版本如何實現錄屏,文中介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們一定要看完!

從網站建設到定制行業解決方案,為提供成都網站設計、做網站服務體系,各種行業企業客戶提供網站建設解決方案,助力業務快速發展。成都創新互聯將不斷加快創新步伐,提供優質的建站服務。
1既然是錄制視頻,我們應該有一個按鈕控制開始和結束。
2在錄制之前,需要先判斷一下Android系統的版本是否大于5.0,并且動態申請一下權限(讀寫,錄音,照相機),這一步可以在點開始按鈕的時候執行
if (ContextCompat.checkSelfPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 102);
}
if (ContextCompat.checkSelfPermission(context, Manifest.permission.RECORD_AUDIO)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.RECORD_AUDIO}, 103);
}
if (ContextCompat.checkSelfPermission(context, Manifest.permission.CAMERA)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.RECORD_AUDIO}, 104);
}
Intent intent = null;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
intent = mediaProjectionManager.createScreenCaptureIntent();
startActivityForResult(intent, 101);//正常情況是要執行到這里的,作用是申請捕捉屏幕
} else {
ShowUtil.showToast(context, "Android版本太低,無法使用該功能");
}3定義MediaProjection和MediaProjectionManager等一些其他必要的變量
boolean isrun = false;//用來標記錄屏的狀態private MediaProjectionManager mediaProjectionManager; private MediaProjection mediaProjection;//錄制視頻的工具private int width, height, dpi;//屏幕寬高和dpi,后面會用到 private ScreenRecorder screenRecorder;//這個是自己寫的錄視頻的工具類,下文會放完整的代碼 Thread thread;//錄視頻要放在線程里去執行
在onCreat里寫好實例化
mediaProjectionManager = (MediaProjectionManager) context.getSystemService(MEDIA_PROJECTION_SERVICE); WindowManager manager = this.getWindowManager(); DisplayMetrics outMetrics = new DisplayMetrics(); manager.getDefaultDisplay().getMetrics(outMetrics); width = outMetrics.widthPixels; height = outMetrics.heightPixels; dpi = outMetrics.densityDpi;
4我們在onActivityResult回調方法中,來處理返回的事件
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == 102) {
Toast.makeText(context, "缺少讀寫權限", Toast.LENGTH_SHORT).show();
return;
}
if (requestCode == 103) {
Toast.makeText(context, "缺少錄音權限", Toast.LENGTH_SHORT).show();
return;
}
if (requestCode == 104) {
Toast.makeText(context, "缺少相機權限", Toast.LENGTH_SHORT).show();
return;
}
if (requestCode != 101) {
Log.e("HandDrawActivity", "error requestCode =" + requestCode);
}
if (resultCode != RESULT_OK) {
Toast.makeText(context, "捕捉屏幕被禁止", Toast.LENGTH_SHORT).show();
return;
}
mediaProjection = mediaProjectionManager.getMediaProjection(resultCode, data);
if (mediaProjection != null) {
screenRecorder = new ScreenRecorder(width, height, mediaProjection, dpi);
}
thread = new Thread() {
@Override
public void run() {
screenRecorder.startRecorder();//跟ScreenRecorder有關的下文再說,總之這句話的意思就是開始錄屏的意思
}
};
thread.start();
binding.startPlayer.setText("停止");//開始和停止我用的同一個按鈕,所以開始錄屏之后把按鈕文字改一下
isrun = true;//錄屏狀態改成真
}5先放上ScreenRecorder代碼,只想要結果的朋友呢,直接把類粘貼走,把報錯的地方改一改(在我自己的項目里可是不報錯的),就實現了錄制屏幕的功能了,還想看看的,可以往下看看
import android.hardware.display.DisplayManager;
import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaFormat;
import android.media.MediaMuxer;
import android.media.MediaRecorder;
import android.media.projection.MediaProjection;
import android.os.Build;
import android.os.Environment;
import android.text.TextUtils;
import android.util.Log;
import android.view.Surface;
import com.coremedia.iso.boxes.Container;
import com.googlecode.mp4parser.authoring.Movie;
import com.googlecode.mp4parser.authoring.Track;
import com.googlecode.mp4parser.authoring.builder.DefaultMp4Builder;
import com.googlecode.mp4parser.authoring.container.mp4.MovieCreator;
import com.googlecode.mp4parser.authoring.tracks.AppendTrack;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
public class ScreenRecorder {
private int mWidth, mHeight, mDensty;
private MediaProjection mediaProjection;
private MediaCodec.BufferInfo mBufferInfo;
private MediaCodec mEncorder;
private Surface mInputSurface;
private MediaMuxer mMuxer;
private boolean isQuit = false;
private boolean mMuxerStarted = false;
private int mTrackIndex;
private String path = Environment.getExternalStorageDirectory().getAbsolutePath() + "/cache";
private MediaRecorder mediaRecorder;
public ScreenRecorder(int mWidth, int mHeight, MediaProjection mediaProjection, int mDensty) {
this.mWidth = mWidth;
this.mHeight = mHeight;
this.mediaProjection = mediaProjection;
this.mDensty = mDensty;
File file = new File(path);
if (!file.exists()) {
file.mkdirs();
}
}
public void startRecorder() {
prepareRecorder();
startLuYin();
startRecording();
}
public void stop() {
isQuit = true;
releaseEncorders(1);
List<String> filePath = new ArrayList<>();
filePath.add(path + "/APlanyinpin.amr");
filePath.add(path + "/APlanshipin.mp4");
joinVideo(filePath, path);
}
public void destory() {
releaseEncorders(0);
}
private void startLuYin() {
File file = new File(path, "APlanyinpin.amr");
mediaRecorder = new MediaRecorder();
mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.DEFAULT);
mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT);
mediaRecorder.setOutputFile(file.getAbsolutePath());
try {
mediaRecorder.prepare();
mediaRecorder.start();
Log.e("HandDrawActivity", "已經開始錄音");
} catch (IOException e) {
e.printStackTrace();
}
}
private void prepareRecorder() {
mBufferInfo = new MediaCodec.BufferInfo(); //元數據,描述bytebuffer的數據,尺寸,偏移
//創建格式化對象 MIMI_TYPE 傳入的 video/avc 是H264編碼格式
MediaFormat format = MediaFormat.createVideoFormat("video/avc", mWidth, mHeight);
int frameRate = 45;
format.setInteger(MediaFormat.KEY_BIT_RATE, 3000000);
format.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);
format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 10);
format.setInteger(MediaFormat.KEY_FRAME_RATE, frameRate);
format.setInteger(MediaFormat.KEY_CHANNEL_COUNT, 1);
format.setInteger(MediaFormat.KEY_CAPTURE_RATE, frameRate);
format.setInteger(MediaFormat.KEY_REPEAT_PREVIOUS_FRAME_AFTER, 1000000 / frameRate);
try {
mEncorder = MediaCodec.createEncoderByType("video/avc");
mEncorder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
mInputSurface = mEncorder.createInputSurface();
mEncorder.start();
} catch (IOException e) {
e.printStackTrace();
releaseEncorders(0);
}
}
private void startRecording() {
File saveFile = new File(path, "APlanshipin.mp4");
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
mMuxer = new MediaMuxer(saveFile.getAbsolutePath(), MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
mediaProjection.createVirtualDisplay("SCREENRECORDER", mWidth, mHeight, mDensty, DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC,
mInputSurface, null, null);
drainEncoder();
}
} catch (Exception e) {
e.printStackTrace();
}
}
private void drainEncoder() {
while (!isQuit) {
Log.e("TAG", "drain.....");
int bufferIndex = mEncorder.dequeueOutputBuffer(mBufferInfo, 0);
if (bufferIndex == MediaCodec.INFO_TRY_AGAIN_LATER) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if (bufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
mTrackIndex = mMuxer.addTrack(mEncorder.getOutputFormat());
if (!mMuxerStarted && mTrackIndex >= 0) {
mMuxer.start();
mMuxerStarted = true;
Log.e("HandDrawActivity", "已經開始錄屏");
}
}
if (bufferIndex >= 0) {
Log.e("TAG", "drain...write..");
ByteBuffer bufferData = mEncorder.getOutputBuffer(bufferIndex);
if ((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) {
mBufferInfo.size = 0;
}
if (mBufferInfo.size != 0) {
if (mMuxerStarted) {
bufferData.position(mBufferInfo.offset);
bufferData.limit(mBufferInfo.offset + mBufferInfo.size);
mMuxer.writeSampleData(mTrackIndex, bufferData, mBufferInfo);
}
}
mEncorder.releaseOutputBuffer(bufferIndex, false);
if ((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
break;
}
}
}
Log.e("HandDrawActivity", "已經結束錄屏");
}
private void releaseEncorders(int i) {
if (mediaProjection != null) {
mediaProjection.stop();
}
mBufferInfo = null;
if (mEncorder != null) {
mEncorder.stop();
}
mInputSurface = null;
if (mMuxer != null && i == 1) {
mMuxer.stop();
}
if (mediaRecorder != null) {
mediaRecorder.stop();
mediaRecorder.reset();
mediaRecorder.release();
}
}
private boolean joinVideo(List<String> filePaths, String resultPath) {
Log.e("HandDrawActivity", "準備合成中");
boolean result = false;
if (filePaths == null || filePaths.size() <= 0 || TextUtils.isEmpty(resultPath)) {
throw new IllegalArgumentException();
}
if (filePaths.size() == 1) { // 只有一個視頻片段,不需要合并
return true;
}
try {
Movie[] inMovies = new Movie[filePaths.size()];
for (int i = 0; i < filePaths.size(); i++) {
Log.e("HandDrawActivity", "filePaths=" + filePaths.get(i));
File f = new File(filePaths.get(i));
if (f.exists()) {
inMovies[i] = MovieCreator.build(filePaths.get(i));
}
}
// 分別取出音軌和視頻
List<Track> videoTracks = new LinkedList<>();
List<Track> audioTracks = new LinkedList<>();
for (Movie m : inMovies) {
for (Track t : m.getTracks()) {
if (t.getHandler().equals("soun")) {
audioTracks.add(t);
}
if (t.getHandler().equals("vide")) {
videoTracks.add(t);
}
}
}
// 合并到最終的視頻文件
Movie outMovie = new Movie();
if (audioTracks.size() > 0) {
outMovie.addTrack(new AppendTrack(audioTracks.toArray(new Track[audioTracks.size()])));
}
if (videoTracks.size() > 0) {
outMovie.addTrack(new AppendTrack(videoTracks.toArray(new Track[videoTracks.size()])));
}
Container mp4file = new DefaultMp4Builder().build(outMovie);
// 將文件輸出
File resultFile = new File(resultPath, "APlanTeacherAnswer.mp4");
if (resultFile.exists() && resultFile.isFile()) {
resultFile.delete();
}
FileChannel fc = new RandomAccessFile(resultFile, "rw").getChannel();
mp4file.writeContainer(fc);
fc.close();
Log.e("HandDrawActivity", "合成完畢");
// 合成完成后把原片段文件刪除
for (String filePath : filePaths) {
File file = new File(filePath);
file.delete();
}
result = true;
HandDrawActivity.sendVideo();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
return result;
}
}6從startRecorder方法說起
public void startRecorder() {
prepareRecorder();//錄視頻前的準備
startLuYin();//直接錄音頻(不用準備)
startRecording();//錄視頻
}錄音的方法
private void startLuYin() {
File file = new File(path, "APlanyinpin.amr");
mediaRecorder = new MediaRecorder();
mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);//聲音來源,麥克
mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.DEFAULT);//音頻格式,默認,其實就是上面定義好的amr了,除此之外還有mp4
mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT);//編碼格式,問題是我不知道編碼格式對什么有影響,是音質高低還是文件大小還是解析快慢,等我有時間去專門研究一下
mediaRecorder.setOutputFile(file.getAbsolutePath());
try {
mediaRecorder.prepare();
mediaRecorder.start();
Log.e("HandDrawActivity", "已經開始錄音");
} catch (IOException e) {
e.printStackTrace();
}
}//錄視頻前的準備工作
private void prepareRecorder() {
mBufferInfo = new MediaCodec.BufferInfo(); //元數據,描述bytebuffer的數據,尺寸,偏移
//創建格式化對象 MIMI_TYPE 傳入的 video/avc 是H264編碼格式
MediaFormat format = MediaFormat.createVideoFormat("video/avc", mWidth, mHeight);
int frameRate = 45;
format.setInteger(MediaFormat.KEY_BIT_RATE, 3000000);
format.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);
format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 10);
format.setInteger(MediaFormat.KEY_FRAME_RATE, frameRate);
format.setInteger(MediaFormat.KEY_CHANNEL_COUNT, 1);
format.setInteger(MediaFormat.KEY_CAPTURE_RATE, frameRate);
format.setInteger(MediaFormat.KEY_REPEAT_PREVIOUS_FRAME_AFTER, 1000000 / frameRate);//編碼器的設置,具體是設置的啥我也不太清楚,但是網上查一查都是這么寫的!!!
try {
mEncorder = MediaCodec.createEncoderByType("video/avc");
mEncorder.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
mInputSurface = mEncorder.createInputSurface();
mEncorder.start();//讓編碼器先跑起來
} catch (IOException e) {
e.printStackTrace();
releaseEncorders(0);
}
}這里也是準備工作
private void startRecording() {
File saveFile = new File(path, "APlanshipin.mp4");
try {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
mMuxer = new MediaMuxer(saveFile.getAbsolutePath(), MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);//百度一下MediaMuxer,講的很詳細的
mediaProjection.createVirtualDisplay("SCREENRECORDER", mWidth, mHeight, mDensty, DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC,
mInputSurface, null, null);
drainEncoder();
}
} catch (Exception e) {
e.printStackTrace();
}
}這個就是開始寫視頻文件了
private void drainEncoder() {
while (!isQuit) {
Log.e("TAG", "drain.....");
int bufferIndex = mEncorder.dequeueOutputBuffer(mBufferInfo, 0);
if (bufferIndex == MediaCodec.INFO_TRY_AGAIN_LATER) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if (bufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
mTrackIndex = mMuxer.addTrack(mEncorder.getOutputFormat());
if (!mMuxerStarted && mTrackIndex >= 0) {
mMuxer.start();
mMuxerStarted = true;
Log.e("HandDrawActivity", "已經開始錄屏");
}
}
if (bufferIndex >= 0) {
Log.e("TAG", "drain...write..");
ByteBuffer bufferData = mEncorder.getOutputBuffer(bufferIndex);
if ((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) {
mBufferInfo.size = 0;
}
if (mBufferInfo.size != 0) {
if (mMuxerStarted) {
bufferData.position(mBufferInfo.offset);
bufferData.limit(mBufferInfo.offset + mBufferInfo.size);
mMuxer.writeSampleData(mTrackIndex, bufferData, mBufferInfo);
}
}
mEncorder.releaseOutputBuffer(bufferIndex, false);
if ((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
break;
}
}
}
Log.e("HandDrawActivity", "已經結束錄屏");
}這個就是把錄好的音頻和視頻合并成mp4的方法了,也是點擊停止錄屏的時候用到的
private boolean joinVideo(List<String> filePaths, String resultPath) {
Log.e("HandDrawActivity", "準備合成中");
boolean result = false;
if (filePaths == null || filePaths.size() <= 0 || TextUtils.isEmpty(resultPath)) {
throw new IllegalArgumentException();
}
if (filePaths.size() == 1) { // 只有一個視頻片段,不需要合并
return true;
}
try {
Movie[] inMovies = new Movie[filePaths.size()];
for (int i = 0; i < filePaths.size(); i++) {
Log.e("HandDrawActivity", "filePaths=" + filePaths.get(i));
File f = new File(filePaths.get(i));
if (f.exists()) {
inMovies[i] = MovieCreator.build(filePaths.get(i));
}
}
// 分別取出音軌和視頻
List<Track> videoTracks = new LinkedList<>();
List<Track> audioTracks = new LinkedList<>();
for (Movie m : inMovies) {
for (Track t : m.getTracks()) {
if (t.getHandler().equals("soun")) {
audioTracks.add(t);
}
if (t.getHandler().equals("vide")) {
videoTracks.add(t);
}
}
}
// 合并到最終的視頻文件
Movie outMovie = new Movie();
if (audioTracks.size() > 0) {
outMovie.addTrack(new AppendTrack(audioTracks.toArray(new Track[audioTracks.size()])));
}
if (videoTracks.size() > 0) {
outMovie.addTrack(new AppendTrack(videoTracks.toArray(new Track[videoTracks.size()])));
}
Container mp4file = new DefaultMp4Builder().build(outMovie);
// 將文件輸出
File resultFile = new File(resultPath, "APlanTeacherAnswer.mp4");
if (resultFile.exists() && resultFile.isFile()) {
resultFile.delete();
}
FileChannel fc = new RandomAccessFile(resultFile, "rw").getChannel();
mp4file.writeContainer(fc);
fc.close();
Log.e("HandDrawActivity", "合成完畢");
// 合成完成后把原片段文件刪除
for (String filePath : filePaths) {
File file = new File(filePath);
file.delete();
}
result = true;
HandDrawActivity.sendVideo();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
return result;
}這個就是結束的時候了,該清空的清空,該注銷的注銷, i是用來判斷錄沒錄的,有可能剛進入這個頁面都沒錄過,直接就返回到別的頁面了,那就有可能空指針異常,因為有些變量都沒初始化,所以用i判斷一下,也可以自己寫別的方法判端
private void releaseEncorders(int i) {
if (mediaProjection != null) {
mediaProjection.stop();
}
mBufferInfo = null;
if (mEncorder != null) {
mEncorder.stop();
}
mInputSurface = null;
if (mMuxer != null && i == 1) {
mMuxer.stop();
}
if (mediaRecorder != null) {
mediaRecorder.stop();
mediaRecorder.reset();
mediaRecorder.release();
}
}以上是“Android5.0以上版本如何實現錄屏”這篇文章的所有內容,感謝各位的閱讀!希望分享的內容對大家有幫助,更多相關知識,歡迎關注創新互聯行業資訊頻道!
本文名稱:Android5.0以上版本如何實現錄屏
文章來源:http://www.yijiale78.com/article14/pjddde.html
成都網站建設公司_創新互聯,為您提供定制網站、App開發、做網站、自適應網站、手機網站建設、面包屑導航
聲明:本網站發布的內容(圖片、視頻和文字)以用戶投稿、用戶轉載內容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網站立場,如需處理請聯系客服。電話:028-86922220;郵箱:631063699@qq.com。內容未經允許不得轉載,或轉載時需注明來源: 創新互聯