【问题标题】:java.lang.IllegalStateException: failed to get surfacejava.lang.IllegalStateException:未能获得表面
【发布时间】:2016-06-13 05:03:15
【问题描述】:

我正在尝试创建一个应用程序,使用户能够记录他的智能手机屏幕。 这是我的起始代码:

   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)

有没有办法解决这个问题?任何建议都会有所帮助并深表感谢。 提前致谢

【问题讨论】:

  • 谁调用了onActivityResult?可能是 MainActivity 得到结果时没有重新创建您的 Activity 状态及其字段的状态吗?您正在调用 mMediaRecorder.getSurface,这似乎是在该部分代码中抛出 IllegalStateException 的唯一方法调用。
  • 你找到答案了吗?我在某些设备上的 Mediarecorder.getSurface() 上也遇到了这个非法状态异常
  • @SunilChaudhary 我认为您遇到的问题是没有 Marshmallow 设备。您找到这个问题的答案了吗?

标签: java android


【解决方案1】:

别在意评论。

我深入研究了文档和您的代码,得到了以下结果。

这是您调用 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(MediaRecorder.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()

当我在集合之前调用集合编码器方法源方法

时,我想我也遇到了一个未记录的错误

编辑:我以为我得到了工作代码,但我仍然得到 IllegalStateExceptions,尽管代码按照文档的顺序排列。

Edit2:我现在开始工作了。可能也不起作用的事情和其他错误消息:

我必须创建一个应用程序可以写入的目录。我无法让外部存储工作,所以我使用了 数据目录。但这与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()时它会导致整个模拟器崩溃。

【讨论】:

  • 为什么 google 不在 MediaRecorder 上创建一个 Builder,它将简化所有这些东西¯_(ツ)_/¯ ?谢谢 ma_cilay
【解决方案2】:

也许您设置了错误的视频大小或错误的视频源。确保mediaRecord.prepare()之前已经执行成功。

我也遇到了这个问题,经过以上检查,我解决了这个问题。

【讨论】:

    【解决方案3】:

    我也遇到了同样的问题。 该问题仅发生一次(在第一次安装游戏并授予权限后) 所以我清除了应用程序的数据(比如千次)以重现错误,但它再也没有发生过。 所以我要做的是从存储中删除文件夹 在您的情况下,文件夹的名称是 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>
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-04-26
      • 2020-01-10
      • 2013-04-19
      • 1970-01-01
      • 2017-07-11
      • 1970-01-01
      • 2018-03-02
      • 1970-01-01
      相关资源
      最近更新 更多