我正在尝试创建一个应用程序,使用户能够记录他的智能手机的屏幕。 这是我的开始代码:
import android.content.Context;
import android.content.Intent;
import android.hardware.display.DisplayManager;
import android.hardware.display.VirtualDisplay;
import android.media.MediaRecorder;
import android.media.projection.MediaProjection;
import android.media.projection.MediaProjectionManager;
import android.os.Environment;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.DisplayMetrics;
import android.util.Log;
import android.widget.Button;
import android.widget.Toast;
import java.io.File;
import java.text.SimpleDateFormat;
import java.util.Date;
public class MainActivity extends AppCompatActivity
{
private static final int CAST_PERMISSION_CODE = 22;
private DisplayMetrics mDisplayMetrics = new DisplayMetrics();
private MediaProjection mMediaProjection;
private VirtualDisplay mVirtualDisplay;
private MediaRecorder mMediaRecorder;
private MediaProjectionManager mProjectionManager;
private Button startButton;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
startButton = (Button) findViewById( R.id.recordButton );
mMediaRecorder = new MediaRecorder();
mProjectionManager = (MediaProjectionManager) getSystemService(Context.MEDIA_PROJECTION_SERVICE);
getWindowManager().getDefaultDisplay().getMetrics(this.mDisplayMetrics);
prepareRecording();
startRecording();
}
private void startRecording() {
// If mMediaProjection is null that means we didn't get a context, lets ask the user
if (mMediaProjection == null) {
// This asks for user permissions to capture the screen
startActivityForResult(mProjectionManager.createScreenCaptureIntent(), CAST_PERMISSION_CODE);
return;
}
mVirtualDisplay = getVirtualDisplay();
mMediaRecorder.start();
}
private void stopRecording() {
if (mMediaRecorder != null) {
mMediaRecorder.stop();
mMediaRecorder.reset();
}
if (mVirtualDisplay != null) {
mVirtualDisplay.release();
}
if (mMediaProjection != null) {
mMediaProjection.stop();
}
prepareRecording();
}
public String getCurSysDate() {
return new SimpleDateFormat("yyyy-MM-dd_HH-mm-ss").format(new Date());
}
private void prepareRecording() {
try {
mMediaRecorder.prepare();
} catch (Exception e) {
e.printStackTrace();
return;
}
final String directory = Environment.getExternalStorageDirectory() + File.separator + "Recordings";
if (!Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
Toast.makeText(this, "Failed to get External Storage", Toast.LENGTH_SHORT).show();
return;
}
final File folder = new File(directory);
boolean success = true;
if (!folder.exists()) {
success = folder.mkdir();
}
String filePath;
if (success) {
String videoName = ("capture_" + getCurSysDate() + ".mp4");
filePath = directory + File.separator + videoName;
} else {
Toast.makeText(this, "Failed to create Recordings directory", Toast.LENGTH_SHORT).show();
return;
}
int width = mDisplayMetrics.widthPixels;
int height = mDisplayMetrics.heightPixels;
mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
mMediaRecorder.setVideoEncodingBitRate(512 * 1000);
mMediaRecorder.setVideoFrameRate(30);
mMediaRecorder.setVideoSize(width, height);
mMediaRecorder.setOutputFile(filePath);
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode != CAST_PERMISSION_CODE) {
// Where did we get this request from ? -_-
//Log.w(TAG, "Unknown request code: " + requestCode);
return;
}
if (resultCode != RESULT_OK) {
Toast.makeText(this, "Screen Cast Permission Denied :(", Toast.LENGTH_SHORT).show();
return;
}
mMediaProjection = mProjectionManager.getMediaProjection(resultCode, data);
// TODO Register a callback that will listen onStop and release & prepare the recorder for next recording
// mMediaProjection.registerCallback(callback, null);
mVirtualDisplay = getVirtualDisplay();
mMediaRecorder.start();
}
private VirtualDisplay getVirtualDisplay()
{
int screenDensity = mDisplayMetrics.densityDpi;
int width = mDisplayMetrics.widthPixels;
int height = mDisplayMetrics.heightPixels;
return mMediaProjection.createVirtualDisplay(this.getClass().getSimpleName(), width, height, screenDensity, DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR, mMediaRecorder.getSurface(), null /*Callbacks*/, null /*Handler*/);
}
}
在显示一条消息,告知用户屏幕捕获功能后,我的应用程序会崩溃。
java.lang.RuntimeException: Failure delivering result ResultInfo{who=null, request=22, result=-1, data=Intent { (has extras) }} to activity {gr.awm.clrecorder/gr.awm.clrecorder.MainActivity}: java.lang.IllegalStateException: failed to get surface
at android.app.ActivityThread.deliverResults(ActivityThread.java:3974)
at android.app.ActivityThread.handleSendResult(ActivityThread.java:4017)
at android.app.ActivityThread.access$1400(ActivityThread.java:172)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1471)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:145)
at android.app.ActivityThread.main(ActivityThread.java:5832)
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:1399)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1194)
Caused by: java.lang.IllegalStateException: failed to get surface
at android.media.MediaRecorder.getSurface(Native Method)
at gr.awm.clrecorder.MainActivity.getVirtualDisplay(MainActivity.java:148)
at gr.awm.clrecorder.MainActivity.onActivityResult(MainActivity.java:135)
有没有办法解决这个问题?任何建议都会有所帮助并深表感谢。 提前致谢
答案 0 :(得分:5)
没关系评论btw。
我深入研究了文档和代码,得到了以下结果。
这是您调用mMediaRecorder方法获取曲面的顺序。
mMediaRecorder.prepare();
mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
mMediaRecorder.setAudioEncoder(Me
diaRecorder.AudioEncoder.AMR_NB);
mMediaRecorder.setVideoEncodingBitRate(512 * 1000);
mMediaRecorder.setVideoFrameRate(30);
mMediaRecorder.setVideoSize(width, height);
mMediaRecorder.setOutputFile(filePath);
这是documentation所说的
//Call this method before prepare().
setVideoEncodingBitRate(); //no exception thrown
//Must be called after setVideoSource(). Call this after setOutFormat() but before prepare().
setVideoSize(width, height); //IllegalStateException if it is called after prepare() or before setOutputFormat()
//Call this only before setOutputFormat().
setAudioSource(); //IllegalStateException if it is called after setOutputFormat()
setVideoSource(); //IllegalStateException if it is called after setOutputFormat()
//Call this after setOutputFormat() and before prepare().
setVideoEncoder(); //IllegalStateException if it is called before setOutputFormat() or after prepare()
setAudioEncoder(); //IllegalStateException if it is called before setOutputFormat() or after prepare().
//Call this after setAudioSource()/setVideoSource() but before prepare().
setOutputFormat(); //IllegalStateException if it is called after prepare() or before setAudioSource()/setVideoSource().
//Call this after setOutputFormat() but before prepare().
setOutputFile(); //IllegalStateException if it is called before setOutputFormat() or after prepare()
//Must be called after setVideoSource(). Call this after setOutFormat() but before prepare().
setVideoFrameRate(); //IllegalStateException if it is called after prepare() or before setOutputFormat().
//This method must be called after setting up the desired audio and video sources, encoders, file format, etc., but before start()
prepare() //IllegalStateException if it is called after start() or before setOutputFormat().
因此,为了使mMediaRecorder处于正确状态,您必须按此顺序调用方法:
setAudioSource
setVideoSource
setOutputFormat
setAudioEncoder
setVideoEncoder
setVideoSize
setVideoFrameRate
setOutputFile
setVideoEncodingBitRate
prepare
start
我认为在设置源方法
之前调用set 编码器方法时,我也遇到了无证错误编辑:我以为我有工作代码,但我仍然得到IllegalStateExceptions,尽管代码是文档的顺序。
Edit2:我现在就开始工作了。可能也不起作用的事情和额外的错误消息:
我必须创建一个App可以写入的目录。我无法让外部存储工作,所以我使用了
数据目录。但这与mMediaRecorder
代码
此代码有效:
private void prepareRecording() {
//Deal with FileDescriptor and Directory here
//Took audio out because emulator has no mic
//mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
mMediaRecorder.setVideoEncodingBitRate(512 * 1000);
//mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB);
mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
mMediaRecorder.setVideoSize(width, height);
mMediaRecorder.setVideoFrameRate(30);
mMediaRecorder.setOutputFile(filePath);
try {
mMediaRecorder.prepare();
} catch (Exception e) {
e.printStackTrace();
return;
}
//Field variable to hold surface object
//Deal with it as you see fit
surface = mMediaRecorder.getSurface();
注意虽然上述代码可以正确创建MediaRecorder
并写入存储,但在调用mMediaRecorder.stop()
时会崩溃整个模拟器。
答案 1 :(得分:0)
也许您设置了错误的视频大小或错误的视频源。确保mediaRecord.prepare()
之前已成功执行。
我也遇到了问题,经过以上检查,我解决了问题。
答案 2 :(得分:0)
我也遇到了同样的问题。 该问题仅发生一次(第一次安装游戏后并授予权限后) 因此我清除了应用程序的数据(例如千分之一)以重现该错误,但此错误再也没有发生。 所以我要做的就是从存储中删除文件夹 在您的情况下,文件夹的名称为String directory =“ Recordings”
这次我要重现错误。
我要解决的问题是,确保在接受“ WRITE_EXTERNAL_STORAGE”权限之后并在调用所有MediaRecorder配置之前创建文件夹
switch (requestCode) {
case REQUEST_PERMISSIONS: {
if ((grantResults.length > 0) && (grantResults[0] +
grantResults[1]) == PackageManager.PERMISSION_GRANTED) {
//onToggleScreenShare(mToggleButton);
File folder = new File(Environment.getExternalStorageDirectory() +
File.separator + "textingstories");
boolean success = true;
if (!folder.exists()) {
success = folder.mkdirs();
if (success) {
// Do something on success
StartRecord();
} else {
// Do something else on failure
}
}
else {
StartRecord();
}
对于Android 10,您可能需要将此文件添加到清单中,以确保已创建文件夹
<manifest ... >
<application android:requestLegacyExternalStorage="true" ... >
...
</application>
</manifest>