我正在尝试使用MediaProjection在Android 5.0棒棒糖中录制手机屏幕,并保存录制的视频。
我尝试了这个代码,但在我将saveFile(uri)
添加到onActivityResult
之后,当我开始录制时,应用程序崩溃了。
获取录制的视频并保存视频的正确方法是什么?
public class MainActivity extends Activity {
private static final String TAG = "MediaProjectionDemo";
private static final int PERMISSION_CODE = 1;
private static final List<Resolution> RESOLUTIONS = new ArrayList<Resolution>() {{
add(new Resolution(640,360));
add(new Resolution(960,540));
add(new Resolution(1366,768));
add(new Resolution(1600,900));
}};
private int mScreenDensity;
private MediaProjectionManager mProjectionManager;
private int mDisplayWidth;
private int mDisplayHeight;
private boolean mScreenSharing;
private MediaProjection mMediaProjection;
private VirtualDisplay mVirtualDisplay;
private Surface mSurface;
private SurfaceView mSurfaceView;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
DisplayMetrics metrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
mScreenDensity = metrics.densityDpi;
mSurfaceView = (SurfaceView) findViewById(R.id.surface);
mSurface = mSurfaceView.getHolder().getSurface();
mProjectionManager =
(MediaProjectionManager) getSystemService(Context.MEDIA_PROJECTION_SERVICE);
ArrayAdapter<Resolution> arrayAdapter = new ArrayAdapter<Resolution>(
this, android.R.layout.simple_list_item_1, RESOLUTIONS);
Spinner s = (Spinner) findViewById(R.id.spinner);
s.setAdapter(arrayAdapter);
s.setOnItemSelectedListener(new ResolutionSelector());
s.setSelection(0);
}
@Override
public void onDestroy() {
super.onDestroy();
if (mMediaProjection != null) {
mMediaProjection.stop();
mMediaProjection = null;
}
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode != PERMISSION_CODE) {
Log.e(TAG, "Unknown request code: " + requestCode);
return;
}
if (resultCode != RESULT_OK) {
Toast.makeText(this,
"User denied screen sharing permission", Toast.LENGTH_SHORT).show();
return;
}
try {
java.net.URI uri = new java.net.URI(data.getData().toString());
saveFile(uri);
} catch (URISyntaxException e) {
e.printStackTrace();
}
mMediaProjection = mProjectionManager.getMediaProjection(resultCode, data);
mVirtualDisplay = createVirtualDisplay();
}
void saveFile(URI sourceUri)
{
String sourceFilename= sourceUri.getPath();
String destinationFilename = android.os.Environment.getExternalStorageDirectory().getPath()+ File.separatorChar+"abc.mp4";
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
try {
bis = new BufferedInputStream(new FileInputStream(sourceFilename));
bos = new BufferedOutputStream(new FileOutputStream(destinationFilename, false));
byte[] buf = new byte[1024];
bis.read(buf);
do {
bos.write(buf);
} while(bis.read(buf) != -1);
} catch (IOException e) {
} finally {
try {
if (bis != null) bis.close();
if (bos != null) bos.close();
} catch (IOException e) {
}
}
}
public void onToggleScreenShare(View view) {
if (((ToggleButton) view).isChecked()) {
shareScreen();
} else {
stopScreenSharing();
}
}
private void shareScreen() {
mScreenSharing = true;
if (mSurface == null) {
return;
}
if (mMediaProjection == null) {
startActivityForResult(mProjectionManager.createScreenCaptureIntent(),
PERMISSION_CODE);
return;
}
mVirtualDisplay = createVirtualDisplay();
}
private void stopScreenSharing() {
mScreenSharing = false;
if (mVirtualDisplay == null) {
return;
}
mVirtualDisplay.release();
}
private VirtualDisplay createVirtualDisplay() {
return mMediaProjection.createVirtualDisplay("ScreenSharingDemo",
mDisplayWidth, mDisplayHeight, mScreenDensity,
DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
mSurface, null /*Callbacks*/, null /*Handler*/);
}
private void resizeVirtualDisplay() {
if (mVirtualDisplay == null) {
return;
}
mVirtualDisplay.resize(mDisplayWidth, mDisplayHeight, mScreenDensity);
}
private class ResolutionSelector implements Spinner.OnItemSelectedListener {
@Override
public void onItemSelected(AdapterView<?> parent, View v, int pos, long id) {
Resolution r = (Resolution) parent.getItemAtPosition(pos);
ViewGroup.LayoutParams lp = mSurfaceView.getLayoutParams();
if (getResources().getConfiguration().orientation
== Configuration.ORIENTATION_LANDSCAPE) {
mDisplayHeight = r.y;
mDisplayWidth = r.x;
} else {
mDisplayHeight = r.x;
mDisplayWidth = r.y;
}
lp.height = mDisplayHeight;
lp.width = mDisplayWidth;
mSurfaceView.setLayoutParams(lp);
}
@Override
public void onNothingSelected(AdapterView<?> parent) { /* Ignore */ }
}
private class MediaProjectionCallback extends MediaProjection.Callback {
@Override
public void onStop() {
mMediaProjection = null;
stopScreenSharing();
}
}
private class SurfaceCallbacks implements SurfaceHolder.Callback {
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
mDisplayWidth = width;
mDisplayHeight = height;
resizeVirtualDisplay();
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
mSurface = holder.getSurface();
if (mScreenSharing) {
shareScreen();
}
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
if (!mScreenSharing) {
stopScreenSharing();
}
}
}
private static class Resolution {
int x;
int y;
public Resolution(int x, int y) {
this.x = x;
this.y = y;
}
@Override
public String toString() {
return x + "x" + y;
}
}
}
logcat:
java.lang.RuntimeException: Failure delivering result ResultInfo{who=null, request=1, result=-1, data=Intent { (has extras) }} to activity {com.name.name/com.name.name.MainActivity}: java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String android.net.Uri.toString()' on a null object reference
at android.app.ActivityThread.deliverResults(ActivityThread.java:3539)
at android.app.ActivityThread.handleSendResult(ActivityThread.java:3582)
at android.app.ActivityThread.access$1300(ActivityThread.java:144)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1327)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:135)
at android.app.ActivityThread.main(ActivityThread.java:5221)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:899)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:694)
Caused by: java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String android.net.Uri.toString()' on a null object reference
at com.name.name.MainActivity.onActivityResult(MainActivity.java:103) // java.net.URI uri = new java.net.URI(data.getData().toString());
at android.app.Activity.dispatchActivityResult(Activity.java:6135)
at android.app.ActivityThread.deliverResults(ActivityThread.java:3535)
at android.app.ActivityThread.handleSendResult(ActivityThread.java:3582)
at android.app.ActivityThread.access$1300(ActivityThread.java:144)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1327)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:135)
at android.app.ActivityThread.main(ActivityThread.java:5221)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:899)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:694)
当您调用startActivityOnResult
时,它会在设备上显示一个确认对话框。一旦对此对话框做出反应,就会调用onActivityResult
。如果是resultCode == RESULT_OK
,并不意味着您已开始录制,它只是意味着您已被授予捕获屏幕的权限(将屏幕镜像到"活动"中)。
您现在需要做的是使用MediaRecorder从活动中已有的表面开始录制视频。