android MediaCodec 录屏时计算实时时长功能实现完整代码
时间: 2023-08-11 20:08:29 浏览: 195
Android5.0以上版本录屏实现代码(完整代码)
以下是一个简单的示例代码,用于使用MediaCodec录制屏幕并计算实时时长:
```java
public class ScreenRecorder {
private static final String TAG = "ScreenRecorder";
private static final String MIME_TYPE = "video/avc";
private static final int FRAME_RATE = 30;
private static final int I_FRAME_INTERVAL = 10;
private static final int TIMEOUT_US = 10000;
private MediaProjection mMediaProjection;
private MediaCodec mMediaCodec;
private MediaMuxer mMuxer;
private Surface mInputSurface;
private int mTrackIndex = -1;
private boolean mMuxerStarted = false;
private long mStartTime;
private long mLastPresentationTimeUs;
private long mElapsedRealtimeOffset;
private Handler mHandler;
private Runnable mCallback;
public ScreenRecorder(MediaProjection mediaProjection) {
mMediaProjection = mediaProjection;
}
public void start(String outputPath) throws IOException {
prepareEncoder(outputPath);
mMediaProjection.registerCallback(mProjectionCallback, mHandler);
mMediaProjection.createVirtualDisplay(
"Recording Display", DisplayMetrics.DENSITY_MEDIUM,
DisplayMetrics.DENSITY_MEDIUM, DisplayMetrics.DENSITY_DEFAULT,
DisplayManager.VIRTUAL_DISPLAY_FLAG_PRESENTATION, mInputSurface,
null, null);
}
public void stop() {
if (mMediaProjection != null) {
mMediaProjection.unregisterCallback(mProjectionCallback);
mMediaProjection.stop();
mMediaProjection = null;
}
releaseEncoder();
}
public void setHandler(Handler handler, Runnable callback) {
mHandler = handler;
mCallback = callback;
}
private void prepareEncoder(String outputPath) throws IOException {
MediaFormat format = MediaFormat.createVideoFormat(MIME_TYPE, getScreenWidth(), getScreenHeight());
format.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);
format.setInteger(MediaFormat.KEY_BIT_RATE, getBitRate());
format.setInteger(MediaFormat.KEY_FRAME_RATE, FRAME_RATE);
format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, I_FRAME_INTERVAL);
mMediaCodec = MediaCodec.createEncoderByType(MIME_TYPE);
mMediaCodec.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
mInputSurface = mMediaCodec.createInputSurface();
mMediaCodec.start();
mMuxer = new MediaMuxer(outputPath, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
}
private void releaseEncoder() {
if (mMediaCodec != null) {
mMediaCodec.stop();
mMediaCodec.release();
mMediaCodec = null;
}
if (mMuxer != null) {
if (mMuxerStarted) {
mMuxer.stop();
}
mMuxer.release();
mMuxer = null;
mMuxerStarted = false;
}
if (mInputSurface != null) {
mInputSurface.release();
mInputSurface = null;
}
}
private int getScreenWidth() {
DisplayMetrics displayMetrics = new DisplayMetrics();
WindowManager windowManager = (WindowManager) App.getContext().getSystemService(Context.WINDOW_SERVICE);
windowManager.getDefaultDisplay().getMetrics(displayMetrics);
return displayMetrics.widthPixels;
}
private int getScreenHeight() {
DisplayMetrics displayMetrics = new DisplayMetrics();
WindowManager windowManager = (WindowManager) App.getContext().getSystemService(Context.WINDOW_SERVICE);
windowManager.getDefaultDisplay().getMetrics(displayMetrics);
return displayMetrics.heightPixels;
}
private int getBitRate() {
return getScreenWidth() * getScreenHeight() * FRAME_RATE * 5;
}
private void drainEncoder(boolean endOfStream) {
if (endOfStream) {
mMediaCodec.signalEndOfInputStream();
}
ByteBuffer[] encoderOutputBuffers = mMediaCodec.getOutputBuffers();
while (true) {
int index = mMediaCodec.dequeueOutputBuffer(new MediaCodec.BufferInfo(), TIMEOUT_US);
if (index == MediaCodec.INFO_TRY_AGAIN_LATER) {
if (!endOfStream) {
break;
}
} else if (index == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
MediaFormat format = mMediaCodec.getOutputFormat();
mTrackIndex = mMuxer.addTrack(format);
mMuxer.start();
mMuxerStarted = true;
} else if (index < 0) {
Log.e(TAG, "Unexpected result from dequeueOutputBuffer: " + index);
} else {
ByteBuffer encodedData = encoderOutputBuffers[index];
if (encodedData == null) {
throw new RuntimeException("encoderOutputBuffer " + index + " was null");
}
if ((mLastPresentationTimeUs == 0 && mStartTime != 0) || (mLastPresentationTimeUs != 0 && mStartTime == 0)) {
mStartTime = System.currentTimeMillis();
mElapsedRealtimeOffset = SystemClock.elapsedRealtime();
}
if (mStartTime != 0) {
long presentationTimeUs = System.currentTimeMillis() - mStartTime + mElapsedRealtimeOffset;
MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo();
bufferInfo.flags = encodedData.get(4);
bufferInfo.offset = 0;
bufferInfo.size = encodedData.limit();
bufferInfo.presentationTimeUs = presentationTimeUs * 1000;
mMuxer.writeSampleData(mTrackIndex, encodedData, bufferInfo);
mLastPresentationTimeUs = bufferInfo.presentationTimeUs;
if (mHandler != null && mCallback != null) {
mHandler.post(mCallback);
}
}
mMediaCodec.releaseOutputBuffer(index, false);
if ((encoderOutputBuffers[index].get(0) & MediaCodec.BUFFER_FLAG_END_OF_STREAM) != 0) {
if (!endOfStream) {
Log.e(TAG, "unexpectedly reached end of stream");
} else {
mMuxerStarted = false;
}
break;
}
}
}
}
private MediaProjection.Callback mProjectionCallback = new MediaProjection.Callback() {
@Override
public void onStop() {
releaseEncoder();
}
};
}
```
在此代码中,我们使用MediaCodec录制屏幕,并使用MediaMuxer将录制的视频保存到文件中。我们还使用了一个Handler和Runnable回调,以便在录制过程中计算实时时长。
要使用ScreenRecorder类,您需要首先获取MediaProjection对象。这可以通过调用MediaProjectionManager.createScreenCaptureIntent()方法来完成。一旦您拥有了MediaProjection对象,您可以使用ScreenRecorder.start()方法开始录制,并使用ScreenRecorder.stop()方法停止录制。
下面是一个使用ScreenRecorder类的简单示例:
```java
private MediaProjectionManager mMediaProjectionManager;
private ScreenRecorder mScreenRecorder;
private Handler mHandler;
private void startScreenRecording() {
mMediaProjectionManager = (MediaProjectionManager) getSystemService(Context.MEDIA_PROJECTION_SERVICE);
startActivityForResult(mMediaProjectionManager.createScreenCaptureIntent(), REQUEST_CODE_SCREEN_CAPTURE);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_CODE_SCREEN_CAPTURE && resultCode == RESULT_OK) {
mScreenRecorder = new ScreenRecorder(mMediaProjectionManager.getMediaProjection(resultCode, data));
try {
mScreenRecorder.setHandler(mHandler, new Runnable() {
@Override
public void run() {
long elapsedRealtime = SystemClock.elapsedRealtime();
long elapsedRealtimeSinceStart = elapsedRealtime - mScreenRecorder.getStartTime();
long elapsedRealtimeSinceLastCallback = elapsedRealtime - mScreenRecorder.getLastCallbackTime();
Log.d(TAG, "Elapsed real time since start: " + elapsedRealtimeSinceStart + " ms");
Log.d(TAG, "Elapsed real time since last callback: " + elapsedRealtimeSinceLastCallback + " ms");
}
});
mScreenRecorder.start(getVideoFilePath());
} catch (IOException e) {
e.printStackTrace();
}
}
}
private void stopScreenRecording() {
if (mScreenRecorder != null) {
mScreenRecorder.stop();
mScreenRecorder = null;
}
}
@Override
protected void onDestroy() {
super.onDestroy();
stopScreenRecording();
}
```
在这个示例中,我们使用了一个Handler和Runnable回调来计算实时时长。当我们调用ScreenRecorder.start()方法时,我们将这些回调传递给ScreenRecorder对象。然后,每次录制一个新帧时,我们将在Runnable回调中更新计时器。
阅读全文