package
com.unionpay.service;
import
java.io.IOException;
import
java.nio.ByteBuffer;
import
java.util.concurrent.atomic.AtomicBoolean;
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.util.Log;
import
android.view.Surface;
public
class
ScreenRecordService
extends
Thread{
private
static
final
String TAG =
"ScreenRecordService"
;
private
int
mWidth;
private
int
mHeight;
private
int
mBitRate;
private
int
mDpi;
private
String mDstPath;
private
MediaProjection mMediaProjection;
private
static
final
String MIME_TYPE =
"video/avc"
;
private
static
final
int
FRAME_RATE =
30
;
private
static
final
int
IFRAME_INTERVAL =
10
;
private
static
final
int
TIMEOUT_US =
10000
;
private
MediaCodec mEncoder;
private
Surface mSurface;
private
MediaMuxer mMuxer;
private
boolean
mMuxerStarted =
false
;
private
int
mVideoTrackIndex = -
1
;
private
AtomicBoolean mQuit =
new
AtomicBoolean(
false
);
private
MediaCodec.BufferInfo mBufferInfo =
new
MediaCodec.BufferInfo();
private
VirtualDisplay mVirtualDisplay;
public
ScreenRecordService(
int
width,
int
height,
int
bitrate,
int
dpi, MediaProjection mp, String dstPath) {
super
(TAG);
mWidth = width;
mHeight = height;
mBitRate = bitrate;
mDpi = dpi;
mMediaProjection = mp;
mDstPath = dstPath;
}
/**
* stop task
*/
public
final
void
quit() {
mQuit.set(
true
);
}
@Override
public
void
run() {
try
{
try
{
prepareEncoder();
mMuxer =
new
MediaMuxer(mDstPath, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
}
catch
(IOException e) {
throw
new
RuntimeException(e);
}
mVirtualDisplay = mMediaProjection.createVirtualDisplay(TAG +
"-display"
, mWidth, mHeight, mDpi,
DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC, mSurface,
null
,
null
);
Log.d(TAG,
"created virtual display: "
+ mVirtualDisplay);
recordVirtualDisplay();
}
finally
{
release();
}
}
private
void
recordVirtualDisplay() {
while
(!mQuit.get()) {
int
index = mEncoder.dequeueOutputBuffer(mBufferInfo, TIMEOUT_US);
if
(index == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
resetOutputFormat();
}
else
if
(index == MediaCodec.INFO_TRY_AGAIN_LATER) {
try
{
Thread.sleep(
10
);
}
catch
(InterruptedException e) {
}
}
else
if
(index >=
0
) {
if
(!mMuxerStarted) {
throw
new
IllegalStateException(
"MediaMuxer dose not call addTrack(format) "
);
}
encodeToVideoTrack(index);
mEncoder.releaseOutputBuffer(index,
false
);
}
}
}
/**
* 硬解码获取实时帧数据并写入mp4文件
*
* @param index
*/
private
void
encodeToVideoTrack(
int
index) {
ByteBuffer encodedData = mEncoder.getOutputBuffer(index);
if
((mBufferInfo.flags & MediaCodec.BUFFER_FLAG_CODEC_CONFIG) !=
0
) {
Log.d(TAG,
"ignoring BUFFER_FLAG_CODEC_CONFIG"
);
mBufferInfo.size =
0
;
}
if
(mBufferInfo.size ==
0
) {
Log.d(TAG,
"info.size == 0, drop it."
);
encodedData =
null
;
}
else
{
}
if
(encodedData !=
null
) {
mMuxer.writeSampleData(mVideoTrackIndex, encodedData, mBufferInfo);
}
}
private
void
resetOutputFormat() {
if
(mMuxerStarted) {
throw
new
IllegalStateException(
"output format already changed!"
);
}
MediaFormat newFormat = mEncoder.getOutputFormat();
mVideoTrackIndex = mMuxer.addTrack(newFormat);
mMuxer.start();
mMuxerStarted =
true
;
Log.i(TAG,
"started media muxer, videoIndex="
+ mVideoTrackIndex);
}
private
void
prepareEncoder()
throws
IOException {
MediaFormat format = MediaFormat.createVideoFormat(MIME_TYPE, mWidth, mHeight);
format.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);
format.setInteger(MediaFormat.KEY_BIT_RATE, mBitRate);
format.setInteger(MediaFormat.KEY_FRAME_RATE, FRAME_RATE);
format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, IFRAME_INTERVAL);
Log.d(TAG,
"created video format: "
+ format);
mEncoder = MediaCodec.createEncoderByType(MIME_TYPE);
mEncoder.configure(format,
null
,
null
, MediaCodec.CONFIGURE_FLAG_ENCODE);
mSurface = mEncoder.createInputSurface();
Log.d(TAG,
"created input surface: "
+ mSurface);
mEncoder.start();
}
private
void
release() {
if
(mEncoder !=
null
) {
mEncoder.stop();
mEncoder.release();
mEncoder =
null
;
}
if
(mVirtualDisplay !=
null
) {
mVirtualDisplay.release();
}
if
(mMediaProjection !=
null
) {
mMediaProjection.stop();
}
if
(mMuxer !=
null
) {
mMuxer.stop();
mMuxer.release();
mMuxer =
null
;
}
}
}