流Android屏幕 [英] Stream android screen

查看:187
本文介绍了流Android屏幕的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试 Truiton ScreenCapture 示例,以便使用 MediaProjection

I am trying to fallow Truiton ScreenCapture example, in order to record the device screen using MediaProjection

保存记录时,它可以正常工作

When saving the recording localy it works

    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时,它不是

How ever when changing to work with FileDescriptor it's not

    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()

If not using the LocalServerSocket, then mMediaRecorder.prepare() throw exception, now that I am using it, get exception in the below method in 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 作为eccpected。当我打电话给 mMediaRecorder.start()

This is my Java server, I get socket after mMediaRecorder.prepare() is called and it stuck on inputStream.read as eccpected. The exception in Android happands when I call mMediaRecorder.start()

    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();
    }


推荐答案

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

You have to use LocalServerSocket, below is what partially worked for me, I have a MediaStreamer class which extends MediaRecorder.

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不是实时,可流式格式,因此即使通过套接字捕获数据,通常在音频或视频捕获结束时写入的关键文件头也会丢失(因为套接字不可见本地文件) - 因此无法播放的数据(因此,为什么它在保存为本地文件时工作正常,是可以理解的)。


But the main problem is mp4 is not very easy to stream . The basic problem is that MP4 is not live, streamable format, so even though data is captured via the socket, the crucial file headers which are normally written at the conclusion of an audio or video capture, are missing (because sockets are not seekeable like local files) - hence the unplayable data (And so, why it works fine when saved as local files, is understandable).

没有简单的方法来执行post-处理数据以手动添加文件头。所以解决方案是要么不使用MP4作为录制格式,要么编写类似于 Spydroid 项目

There is no easy way to perform post-processing on the data to manually add the file headers. So the solution is either don't use MP4 as the recording format, or, write a packetiser similar to what is used in the Spydroid project

希望这会有所帮助!

这篇关于流Android屏幕的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆