国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 系統(tǒng) > Android > 正文

Android5.0以上版本錄屏實現(xiàn)代碼(完整代碼)

2019-10-22 18:16:31
字體:
供稿:網(wǎng)友

錄屏的方式是分別錄制音頻和視頻,最后合并成mp4格式,比較麻煩,因為網(wǎng)上完整的教程比較少,所以我打算寫一個完整版的,照著我的代碼寫完之后,至少是能夠?qū)崿F(xiàn)功能的,而不是簡單的介紹下用法。

1既然是錄制視頻,我們應(yīng)該有一個按鈕控制開始和結(jié)束。

2在錄制之前,需要先判斷一下Android系統(tǒng)的版本是否大于android/245290.html">5.0,并且動態(tài)申請一下權(quán)限(讀寫,錄音,照相機),這一步可以在點開始按鈕的時候執(zhí)行  

  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);//正常情況是要執(zhí)行到這里的,作用是申請捕捉屏幕 } else {  ShowUtil.showToast(context, "Android版本太低,無法使用該功能"); }

3定義MediaProjection和MediaProjectionManager等一些其他必要的變量

  boolean isrun = false;//用來標(biāo)記錄屏的狀態(tài)private MediaProjectionManager mediaProjectionManager;  private MediaProjection mediaProjection;//錄制視頻的工具private int width, height, dpi;//屏幕寬高和dpi,后面會用到  private ScreenRecorder screenRecorder;//這個是自己寫的錄視頻的工具類,下文會放完整的代碼  Thread thread;//錄視頻要放在線程里去執(zhí)行

在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回調(diào)方法中,來處理返回的事件

@Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == 102) {  Toast.makeText(context, "缺少讀寫權(quán)限", Toast.LENGTH_SHORT).show();  return; } if (requestCode == 103) {  Toast.makeText(context, "缺少錄音權(quán)限", Toast.LENGTH_SHORT).show();  return; } if (requestCode == 104) {  Toast.makeText(context, "缺少相機權(quán)限", 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有關(guān)的下文再說,總之這句話的意思就是開始錄屏的意思  } }; thread.start(); binding.startPlayer.setText("停止");//開始和停止我用的同一個按鈕,所以開始錄屏之后把按鈕文字改一下 isrun = true;//錄屏狀態(tài)改成真 }

5先放上ScreenRecorder代碼,只想要結(jié)果的朋友呢,直接把類粘貼走,把報錯的地方改一改(在我自己的項目里可是不報錯的),就實現(xiàn)了錄制屏幕的功能了,還想看看的,可以往下看看

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", "已經(jīng)開始錄音"); } catch (IOException e) {  e.printStackTrace(); } } private void prepareRecorder() { mBufferInfo = new MediaCodec.BufferInfo(); //元數(shù)據(jù),描述bytebuffer的數(shù)據(jù),尺寸,偏移 //創(chuàng)建格式化對象 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", "已經(jīng)開始錄屏");  }  }  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", "已經(jīng)結(jié)束錄屏"); } 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", "準(zhǔn)備合成中"); 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();//錄視頻前的準(zhǔn)備 startLuYin();//直接錄音頻(不用準(zhǔn)備) startRecording();//錄視頻 }

錄音的方法

private void startLuYin() { File file = new File(path, "APlanyinpin.amr"); mediaRecorder = new MediaRecorder(); mediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);//聲音來源,麥克 mediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.DEFAULT);//音頻格式,默認(rèn),其實就是上面定義好的amr了,除此之外還有mp4 mediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.DEFAULT);//編碼格式,問題是我不知道編碼格式對什么有影響,是音質(zhì)高低還是文件大小還是解析快慢,等我有時間去專門研究一下 mediaRecorder.setOutputFile(file.getAbsolutePath()); try {  mediaRecorder.prepare();  mediaRecorder.start();  Log.e("HandDrawActivity", "已經(jīng)開始錄音"); } catch (IOException e) {  e.printStackTrace(); } }
//錄視頻前的準(zhǔn)備工作private void prepareRecorder() { mBufferInfo = new MediaCodec.BufferInfo(); //元數(shù)據(jù),描述bytebuffer的數(shù)據(jù),尺寸,偏移 //創(chuàng)建格式化對象 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);//編碼器的設(shè)置,具體是設(shè)置的啥我也不太清楚,但是網(wǎng)上查一查都是這么寫的!!! 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); } }

這里也是準(zhǔn)備工作

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,講的很詳細(xì)的  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", "已經(jīng)開始錄屏");  }  }  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", "已經(jīng)結(jié)束錄屏"); }

這個就是把錄好的音頻和視頻合并成mp4的方法了,也是點擊停止錄屏的時候用到的

private boolean joinVideo(List<String> filePaths, String resultPath) { Log.e("HandDrawActivity", "準(zhǔn)備合成中"); 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; }

這個就是結(jié)束的時候了,該清空的清空,該注銷的注銷, 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(); } }

7部分代碼也是我從網(wǎng)上扒的,但是網(wǎng)上的代碼就沒怎么見過比較完整的版本的,我上面寫的都是經(jīng)過我自己測試絕對沒問題的而且代碼也沒什么遺漏的,要是發(fā)現(xiàn)有遺漏的代碼我后續(xù)再補上。


注:相關(guān)教程知識閱讀請移步到Android開發(fā)頻道。
發(fā)表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發(fā)表
主站蜘蛛池模板: 新邵县| 泰兴市| 庐江县| 湘潭县| 紫云| 乐至县| 临江市| 西充县| 卢湾区| 鹤岗市| 屏山县| 灵川县| 吉木萨尔县| 绿春县| 定陶县| 社会| 南宫市| 阳高县| 来宾市| 天柱县| 敦煌市| 永和县| 天长市| 吉安县| 梧州市| 青川县| 泾源县| 桃江县| 安远县| 吉林省| 马山县| 乾安县| 娄底市| 潜山县| 南岸区| 杭锦旗| 观塘区| 犍为县| 高安市| 交口县| 雅安市|