Android - WebRTC:视频通话实时绘图

时间:2018-04-18 09:05:32

标签: android canvas drawing webrtc

我正在尝试在Android(移动)应用上使用WebRTC制作一个简单的功能。 该应用程序现在可以进行简单的视频通话:将两个设备相互连接,让他们听到和看到。 我想要实现的是在通话期间的一些现场绘画。简单地说:User1调用User2,调用连接,然后User1点击一个绘制按钮,它将冻结视频帧并允许他在这个冻结帧上绘制。显然,User2应该会在手机上看到这个画面。 现在我可以冻结框架(通过调用videoCapture.stopCapture())并使用自定义SurfaceViewRenderer绘制它。问题是User2看不到图形,只看到冻结的框架。

首先,我尝试创建一个新的视频图像,其中包含绘图画布和要绘制的冻结帧,但我无法成功。 使用peerConnectionFactory.createVideoTrack("ARDAMSv1_" + rand, videoSource);创建视频群时 我应该指定轨道的视频源,但源只能是VideoSource,并且此VideoSource只能使用VideoCapturer创建,该docker run -d -it --net bridge --name 5114100123_10.151.36.227 --publish 9001:3128 fourirakbar/debian-squid:version1 直接链接到设备相机(没有任何绘图)它当然)。这就解释了为什么User2没有在他的设备上看到任何图画 我的问题是:如何创建一个VideoCapturer,它可以流式传输相机流(冻结帧)和带有绘图的画布?

所以我尝试将自己的VideoCapturer实现为:
1)捕获视图(例如包含图形和冻结帧的布局)并将其流式传输到VideoSource
或2)捕获摄像机视图,但在流式传输之前也将图形添加到帧中 我无法完成任何这项工作,因为我不知道如何操纵I420Frame对象来绘制它并使用正确的回调返回它。

也许我对这种方法完全错了,需要做一些完全不同的事情,我对任何建议持开放态度。 PS:我正在使用Android API 25和WebRTC 1.0.19742。我不想使用任何付费的第三方SDK / lib。

有没有人知道如何从一个Android应用程序到另一个Android应用程序实现简单的WebRTC实时绘图?

3 个答案:

答案 0 :(得分:1)

几周前我们回到了该功能,我设法找到了一种方法。

我扩展了自己的CameraCapturer类,以在渲染之前获得相机框架的支撑。然后,我创建了自己的CanvasView以便能够在其上进行绘制。

从那里开始,我将两个位图(相机视图+我的画布与图形)合并在一起,然后用OpenGL在缓冲区上绘制它并在SurfaceView上显示它。

如果有人感兴趣,我可能会发布一些代码。

答案 1 :(得分:1)

  @Override
    public void startCapture(int width, int height, int fps) {
    Log.d("InitialsClass", "startCapture");

    surTexture.stopListening();
    cameraHeight = 480;
    cameraWidth = 640;
    int horizontalSpacing = 16;
    int verticalSpacing = 20;
    int x = horizontalSpacing;
    int y = cameraHeight - verticalSpacing;

    cameraBitmap = Bitmap.createBitmap(640, 480, Bitmap.Config.ARGB_8888);

    
    YuvFrame frame = new YuvFrame(null, PROCESSING_NONE, appContext);

    surTexture.startListening(new VideoSink() {
        @Override
        public void onFrame(VideoFrame videoFrame) {
            frame.fromVideoFrame(videoFrame, PROCESSING_NONE);
        }
    });
    if (captureThread == null || !captureThread.isInterrupted()) {

        captureThread = new Thread(() -> {
            try {
                if (matrix == null) {
                    matrix = new Matrix();
                }
                long start = System.nanoTime();
                capturerObs.onCapturerStarted(true);
                int[] textures = new int[1];
                GLES20.glGenTextures(1, textures, 0);

                YuvConverter yuvConverter = new YuvConverter();

                WindowManager windowManager = (WindowManager) appContext.getSystemService(Context.WINDOW_SERVICE);

                GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textures[0]);
                // Set filtering
                GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST);
                GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_NEAREST);
                //The bitmap is drawn on the GPU at this point.
                TextureBufferImpl buffer = new TextureBufferImpl(cameraWidth, cameraHeight - 3, VideoFrame.TextureBuffer.Type.RGB, textures[0], matrix, surTexture.getHandler(), yuvConverter, null);

                Resources resources = appContext.getResources();
                float scale = resources.getDisplayMetrics().density;
                Log.d("InitialsClass before", "camera start capturer width- " + cameraWidth + " height- " + cameraHeight);

                while (!Thread.currentThread().isInterrupted()) {

                    ByteBuffer gBuffer = frame.getByteBuffer();
                    if (gBuffer != null) {
                        Log.d("InitialsClass ", "gBuffer not null");
                        cameraBitmap.copyPixelsFromBuffer(gBuffer);
                    }
                    if (cameraBitmap != null) {

                        if (canvas == null) {
                            canvas = new Canvas();
                        }

                        if (appContext.getResources().getConfiguration().orientation == ORIENTATION_PORTRAIT)
                            rotationDegree = -90;
                        else {
                            assert windowManager != null;
                            if (windowManager.getDefaultDisplay().getRotation() == Surface.ROTATION_0) {
                                // clockwise
                                rotationDegree = 0;
                            } else if (windowManager.getDefaultDisplay().getRotation() == Surface.ROTATION_90) {
                                // anti-clockwise
                                rotationDegree = -180;
                            }
                        }

                        canvas.save(); //save the position of the canvas
                        canvas.rotate(rotationDegree, (cameraBitmap.getWidth() / 2), (cameraBitmap.getHeight() / 2)); //rotate the canvas
                        canvas.drawBitmap(cameraBitmap, 0, 0, null); //draw the image on the rotated canvas
                        canvas.restore();  // restore the canvas position.


                        matrix.setScale(-1, 1);
                        matrix.postTranslate(/*weakBitmap.get().getWidth()*/ cameraBitmap.getWidth(), 0);
                        matrix.setScale(1, -1);
                        matrix.postTranslate(0, /*weakBitmap.get().getHeight()*/ cameraBitmap.getHeight());
                        canvas.setMatrix(matrix);

                        if (textPaint == null) {
                            textPaint = new TextPaint();
                        }
                        textPaint.setColor(Color.WHITE);
                        textPaint.setTypeface(Typeface.create(typeFace, Typeface.BOLD));
                        textPaint.setTextSize((int) (11 * scale));

                        if (textBounds == null) {
                            textBounds = new Rect();
                        }
                        textPaint.getTextBounds(userName, 0, userName.length(), textBounds);



                        textPaint.setTextAlign(Paint.Align.LEFT);
                        textPaint.setAntiAlias(true);
                        canvas.drawText(userName, x, y, textPaint);

                        if (paint == null) {
                            paint = new Paint();
                        }
                        if (isLocalCandidate) {
                            paint.setColor(Color.GREEN);
                        } else {
                            paint.setColor(Color.TRANSPARENT);
                        }
                        paint.setStrokeWidth(8);
                        paint.setStyle(Paint.Style.STROKE);
                        canvas.drawRect(0, 8, cameraWidth - 8, cameraHeight - 8, paint);

                        if (surTexture != null && surTexture.getHandler() != null && surTexture.getHandler().getLooper().getThread().isAlive()) {

                            surTexture.getHandler().post(() -> {

                                // Load the bitmap into the bound texture.
                                GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, /*weakBitmap.get()*/ cameraBitmap, 0);

                                //We transfer it to the VideoFrame
                                VideoFrame.I420Buffer i420Buf = yuvConverter.convert(buffer);
                                long frameTime = System.nanoTime() - start;
                                VideoFrame videoFrame = new VideoFrame(i420Buf, 0, frameTime);

                                capturerObs.onFrameCaptured(videoFrame);
                            });
                        }
                    }
                    Thread.sleep(100);
                }
            } catch (InterruptedException ex) {
                Log.d("InitialsClass camera", ex.toString());
                Thread.currentThread().interrupt();
                return;
            }
        });
    }
    captureThread.start();
}

@阿奈尔。看看吧。

答案 2 :(得分:0)

我正在开发类似的应用程序,所以我分享了在webrtc流上绘制

  1. 您需要做的是获得stream to canvas
  2. 然后有绘图应用程序可以在画布上进行编辑,我看一个William Malone项目。 (如果要导入图片,请先透明!)
  3. 最后(就像我想念的那样)是stream from canvas,就像使用任何WebRTC一样。

我专门为您制作了一个小样here(本地webrtc,请参阅日志)。

PS:我使用的是getDisplayMedia,而不是userMedia,因为我的网络摄像头是 kaput ...