在錄屏基礎上使用MediaMuxer將錄屏保存爲視頻,詳細代碼如下:
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Intent;
import android.hardware.display.DisplayManager;
import android.hardware.display.VirtualDisplay;
import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaFormat;
import android.media.MediaMuxer;
import android.media.projection.MediaProjection;
import android.media.projection.MediaProjectionManager;
import android.os.Bundle;
import android.os.Environment;
import android.util.DisplayMetrics;
import android.view.Surface;
import android.view.View;
import android.widget.Toast;
import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.concurrent.atomic.AtomicBoolean;
public class SaveScreenActivity extends AppCompatActivity {
private static final int REQUEST_CODE = 102;
private MediaProjectionManager projectionManager;
private MediaProjection mediaProjection;
private VirtualDisplay mVirtualDisplay;
MediaCodec mediaCodec;
MediaMuxer mediaMuxer;
Surface surface;
int width;
int height;
int dpi;
private MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
private int videoTrackIndex = -1;
String filePath;
private AtomicBoolean mQuit = new AtomicBoolean(false);
@Override
protected void onDestroy() {
super.onDestroy();
release();
}
private void release() {
if (mediaCodec != null) {
mediaCodec.stop();
mediaCodec.release();
mediaCodec = null;
}
if (mVirtualDisplay != null) {
mVirtualDisplay.release();
mVirtualDisplay = null;
}
if (mediaProjection != null) {
mediaProjection.stop();
mediaProjection = null;
}
if (mediaMuxer != null) {
mediaMuxer.release();
mediaMuxer = null;
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_save_screen);
DisplayMetrics metric = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metric);
width = metric.widthPixels;
height = metric.heightPixels;
dpi = metric.densityDpi;
File file = new File(Environment.getExternalStorageDirectory(),
"record-" + width + "x" + height + "-" + System.currentTimeMillis() + ".mp4");
filePath = file.getAbsolutePath();
projectionManager = (MediaProjectionManager) getSystemService(MEDIA_PROJECTION_SERVICE);
findViewById(R.id.stop).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mQuit.set(true);
Toast.makeText(SaveScreenActivity.this, "Recorder stop", Toast.LENGTH_SHORT).show();
}
});
}
@Override
public void onAttachedToWindow() {
super.onAttachedToWindow();
Intent captureIntent = projectionManager.createScreenCaptureIntent();
startActivityForResult(captureIntent, REQUEST_CODE);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_CODE && resultCode == RESULT_OK) {
mediaProjection = projectionManager.getMediaProjection(resultCode, data);
new Thread() {
@Override
public void run() {
try {
try {
prepareEncoder();
mediaMuxer = new MediaMuxer(filePath, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
} catch (IOException e) {
throw new RuntimeException(e);
}
mVirtualDisplay = createVirtualDisplay();
recordVirtualDisplay();
} finally {
release();
}
}
}.start();
Toast.makeText(this, "Recorder is running...", Toast.LENGTH_SHORT).show();
moveTaskToBack(true);
}
}
/**
* name: 是生成的VirtualDisplay實例的名稱;
* width, height: 分別是生成實例的寬高,必須大於0;
* dpi: 生成實例的像素密度,必須大於0,一般都取1;
* surface: 這個比較重要,是你生成的VirtualDisplay的載體,
* 我的理解是,VirtualDisplay的內容是一幀幀的屏幕截圖(所以你看到是有寬高,像素密度等設置),
* 所以MediaProjection獲取到的其實是一幀幀的圖,然後通過 surface(surface你可以理解成是android的一個畫布,
* 默認它會以每秒60幀來刷新,這裏我們不再展開細說),來順序播放這些圖片,形成視頻。
*
* @return
*/
private VirtualDisplay createVirtualDisplay() {
return mediaProjection.createVirtualDisplay("RecordScreen",
width, height, dpi,
DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
surface, null /*Callbacks*/, null /*Handler*/);
}
private void prepareEncoder() throws IOException {
MediaFormat format = MediaFormat.createVideoFormat("video/avc", width, height);
format.setInteger(MediaFormat.KEY_COLOR_FORMAT,
MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);
format.setInteger(MediaFormat.KEY_BIT_RATE, 6000000);
format.setInteger(MediaFormat.KEY_FRAME_RATE, 30);
format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 10);
mediaCodec = MediaCodec.createEncoderByType("video/avc");
mediaCodec.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
surface = mediaCodec.createInputSurface();
mediaCodec.start();
}
private void recordVirtualDisplay() {
while (!mQuit.get()) {
int index = mediaCodec.dequeueOutputBuffer(bufferInfo, 10000);
if (index == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
resetOutputFormat();
} else if (index >= 0) {
encodeToVideoTrack(index);
mediaCodec.releaseOutputBuffer(index, false);
}
}
}
private void encodeToVideoTrack(int index) {
ByteBuffer encodedData = mediaCodec.getOutputBuffer(index);
if ((bufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) != 0) {
bufferInfo.size = 0;
}
if (bufferInfo.size == 0) {
encodedData = null;
}
if (encodedData != null) {
encodedData.position(bufferInfo.offset);
encodedData.limit(bufferInfo.offset + bufferInfo.size);
mediaMuxer.writeSampleData(videoTrackIndex, encodedData, bufferInfo);
}
}
private void resetOutputFormat() {
MediaFormat newFormat = mediaCodec.getOutputFormat();
videoTrackIndex = mediaMuxer.addTrack(newFormat);
mediaMuxer.start();
}
}
<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" />
<uses-feature
android:glEsVersion="0x00020000"
android:required="true" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />