【问题标题】:Stream android screen流式安卓屏幕
【发布时间】:2017-03-24 06:43:27
【问题描述】:

我正在尝试闲置Truiton ScreenCapture 示例,以便使用MediaProjection 记录设备屏幕

在本地保存录音时,它可以工作

    mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
    mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);

    String localFilePath = getLocalFilePath();
    mMediaRecorder.setOutputFile(localFilePath);

    mMediaRecorder.setVideoSize(DISPLAY_WIDTH, DISPLAY_HEIGHT);
    mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.MPEG_4_SP);
    mMediaRecorder.setVideoFrameRate(30);
    mMediaRecorder.prepare();

当更改为使用 FileDescriptor 时,它不是

    mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
    mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);

    String hostname = "10.26.100.18";
    int port = 2007;
    Socket socket = new Socket(InetAddress.getByName(hostname), port);
    ParcelFileDescriptor fileDescriptor = ParcelFileDescriptor.fromSocket(socket);
    LocalServerSocket localServerSocket = new LocalServerSocket(fileDescriptor.getFileDescriptor());

    mMediaRecorder.setOutputFile(localServerSocket.getFileDescriptor());

    mMediaRecorder.setVideoSize(DISPLAY_WIDTH, DISPLAY_HEIGHT);
    mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.MPEG_4_SP);
    mMediaRecorder.setVideoFrameRate(30);
    mMediaRecorder.prepare();

如果不使用LocalServerSocket,那么mMediaRecorder.prepare()会抛出异常,现在我正在使用,在mMediaRecorder.getSurface()下面的方法中获取异常

private VirtualDisplay createVirtualDisplay() {
    return mMediaProjection.createVirtualDisplay("MainActivity",
            DISPLAY_WIDTH, DISPLAY_HEIGHT, mScreenDensity,
            DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
            mMediaRecorder.getSurface(), null /*Callbacks*/, null
            /*Handler*/);
}

例外

Caused by: java.lang.IllegalStateException: failed to get surface
   at android.media.MediaRecorder.getSurface(Native Method)
   at com.truiton.screencapture.MainActivity$override.createVirtualDisplay(MainActivity.java:172)
   at com.truiton.screencapture.MainActivity$override.onActivityResult(MainActivity.java:133)
   at com.truiton.screencapture.MainActivity$override.access$dispatch(MainActivity.java)
   at com.truiton.screencapture.MainActivity.onActivityResult(MainActivity.java:0)
   at android.app.Activity.dispatchActivityResult(Activity.java:6456)
   at android.app.ActivityThread.deliverResults(ActivityThread.java:3695)
   at android.app.ActivityThread.handleSendResult(ActivityThread.java:3742) 
   at android.app.ActivityThread.-wrap16(ActivityThread.java) 
   at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1393) 
   at android.os.Handler.dispatchMessage(Handler.java:102) 
   at android.os.Looper.loop(Looper.java:148) 
   at android.app.ActivityThread.main(ActivityThread.java:5417) 
   at java.lang.reflect.Method.invoke(Native Method) 
   at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726) 
   at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616) 

这是我的 Java 服务器,在调用 mMediaRecorder.prepare() 后我得到了套接字,并且它卡在了 inputStream.read 上,因为 eccected。当我调用mMediaRecorder.start()时,Android 中的异常发生了

    final byte[] buffer = new byte[1024];
    try {
        ServerSocket serverSocket = new ServerSocket(2007);
        while (!serverSocket.isClosed()) {
            Socket socket = serverSocket.accept();
            File videoFile = createEmptyVideoFile();
            FileOutputStream outputStream = new FileOutputStream(videoFile);
            InputStream inputStream = socket.getInputStream();
            int length = inputStream.read(buffer);
            while (length != -1) {
                outputStream.write(buffer, 0, length);
                length = inputStream.read(buffer);
            }
        }
    } catch (IOException e) {
        e.printStackTrace();
    }

【问题讨论】:

    标签: java android android-mediarecorder android-mediaprojection


    【解决方案1】:

    你必须使用 LocalServerSocket,下面是部分对我有用的,我有一个扩展 MediaRecorder 的 MediaStreamer 类。

    public class MediaStreamer extends MediaRecorder {
    
        private LocalServerSocket localServerSocket = null;
        private LocalSocket receiver, sender = null;
    
        public void prepare() throws IllegalStateException,IOException {
    
            receiver = new LocalSocket();
            try {
                localServerSocket = new LocalServerSocket("<your_socket_addr>");
                receiver.connect(new LocalSocketAddress("<your_socket_addr>"));
                receiver.setReceiveBufferSize(4096);
                receiver.setSendBufferSize(4096);
                sender = localServerSocket.accept();
                sender.setReceiveBufferSize(4096);
                sender.setSendBufferSize(4096); 
            } catch (IOException e1) {
                throw new IOException("Can't create local socket !");
            }
    
            setOutputFile(sender.getFileDescriptor());
    
            try {
                super.prepare();
            } catch (Exception e) {
                closeSockets();
                throw e;
            }           
        }
    
        public InputStream getInputStream() {
    
            InputStream out = null;
    
            try {
                out = receiver.getInputStream();
            } catch (IOException e) {
            }
    
            return out;
    
        }
    
    
        public void stop() {
            closeSockets();
            super.stop();
        }
    
        private void closeSockets() {
            if (localServerSocket !=null) {
                try {
                    localServerSocket.close();
                    sender.close();
                    receiver.close();
                }
                catch (IOException e) {
    
                }
                localServerSocket = null; 
                sender = null; 
                receiver = null;
            }
        }
    }
    

    用于录音

    video = new MediaStreamer();
    video.reset();
    
    video.setVideoSource(MediaRecorder.VideoSource.DEFAULT);
    video.setPreviewDisplay(holder.getSurface());
    video.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
    video.setVideoFrameRate(VideoConstants.frameRate);
    video.setVideoEncodingBitRate(VideoConstants.bitRate*1000);
    video.setVideoSize(VideoConstants.resolationX, VideoConstants.resolationY);
    video.setVideoEncoder(MediaRecorder.VideoEncoder.MPEG_4_SP);
    
    try {
       video.prepare();
    } catch (IOException e) {
       e.printStackTrace();
    }
    video.start();
    


    但主要问题是 mp4 不是很容易流式传输。基本问题是 MP4 不是实时的、可流式传输的格式,因此即使通过套接字捕获数据,通常在音频或视频捕获结束时写入的关键文件头也会丢失(因为套接字不可搜索,例如本地文件) - 因此无法播放数据(因此,为什么保存为本地文件时它可以正常工作,这是可以理解的)。

    没有简单的方法可以对数据进行后处理以手动添加文件头。所以解决办法是要么不使用 MP4 作为录音格式,要么写一个类似于 Spydroid 项目中使用的打包器

    希望这会有所帮助!

    【讨论】:

    • 我的回答不够充分还是需要进一步澄清,请告诉我?
    • 如何在 nodejs 中找到 'your_socket_addr' - easyRTC 架构?
    • @AbhinavPuri 您推荐哪种格式的流式传输?
    猜你喜欢
    • 2011-06-25
    • 2011-06-28
    • 2011-09-08
    • 2013-04-27
    • 2011-06-09
    • 2013-03-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多