Отправка изображения из приложения Android в веб-приложение с помощью webrtc

Я следую этим двум примерам кода: один для отправки изображения с Android и другой для прикрепления полученного изображения на холсте.

Для отправки изображения с Android по каналу данных webrtc

https://github.com/Temasys/skylink-android-sharing-screen/blob/master/SkylinkShare/app/src/main/java/skylink/temasys/com/sg/skylinkshare/MainActivity.java

Для получения изображения в Интернете и прикрепления к холсту с использованием канала данных webrtc

https://io2014codelabs.appspot.com/static/codelabs/webrtc-file-sharing/#7

Дело в том, что я хочу постоянно отправлять изображения экрана с Android в Интернет, чтобы казалось, что этот экран передается с Android, и каждое изменение на экране Android будет отображаться на холсте в Интернете.

Код на Android

Это код для захвата экрана Android.

public void startProjection() {
   startActivityForResult(projectionManager.createScreenCaptureIntent(), SCREEN_REQUEST_CODE);
}

Это код для извлечения изображений с экрана Android, которые я только что захватил.

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    switch (requestCode) {
        case SCREEN_REQUEST_CODE:
            mediaProjection = projectionManager.getMediaProjection(resultCode, data);
            if (mediaProjection != null) {

                projectionStarted = true;

                // Initialize the media projection
                DisplayMetrics metrics = getResources().getDisplayMetrics();
                int density = metrics.densityDpi;
                int flags = DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY
                        | DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC;

                Display display = getWindowManager().getDefaultDisplay();
                Point size = new Point();
                display.getSize(size);

                projectionDisplayWidth = size.x;
                projectionDisplayHeight = size.y;

                imageReader = ImageReader.newInstance(projectionDisplayWidth, projectionDisplayHeight
                        , PixelFormat.RGBA_8888, 2);
                mediaProjection.createVirtualDisplay("screencap",
                        projectionDisplayWidth, projectionDisplayHeight, density,
                        flags, imageReader.getSurface(), null, handler);
                imageReader.setOnImageAvailableListener(new ImageAvailableListener(), handler);
            }
            break;
    }
}

Вот доступный класс прослушивателя изображений:

private class ImageAvailableListener implements ImageReader.OnImageAvailableListener {
    @Override
    public void onImageAvailable(ImageReader reader) {
        Image image = null;
        FileOutputStream fos = null;
        Bitmap bitmap = null;

        ByteArrayOutputStream stream = null;

        try {
            image = imageReader.acquireLatestImage();
            if (image != null) {
                Image.Plane[] planes = image.getPlanes();
                ByteBuffer buffer = planes[0].getBuffer();
                int pixelStride = planes[0].getPixelStride();
                int rowStride = planes[0].getRowStride();
                int rowPadding = rowStride - pixelStride * projectionDisplayWidth;

                // create bitmap
                bitmap = Bitmap.createBitmap(projectionDisplayWidth + rowPadding / pixelStride,
                        projectionDisplayHeight, Bitmap.Config.ARGB_8888);
                bitmap.copyPixelsFromBuffer(buffer);

                stream = new ByteArrayOutputStream();
                bitmap.compress(Bitmap.CompressFormat.JPEG, 5, stream);


                ByteBuffer byteBuffer = ByteBuffer.wrap(stream.toByteArray());
                DataChannel.Buffer buf = new DataChannel.Buffer(byteBuffer, true);

                Log.w("CONFERENCE_SCREEN", "Image size less than chunk size condition");

                client.sendDataChannelMessage(buf);

                imagesProduced++;
                Log.w("CONFERENCE_SCREEN", "captured image: " + imagesProduced);
            }

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (fos != null) {
                try {
                    fos.close();
                } catch (IOException ioe) {
                    ioe.printStackTrace();
                }
            }

            if (stream != null) {
                try {
                    stream.close();
                } catch (IOException ioe) {
                    ioe.printStackTrace();
                }
            }

            if (bitmap != null) {
                bitmap.recycle();
            }

            if (image != null) {
                image.close();
            }
        }
    }
}

Код в Интернете

Создание холста:

var canvas = document.createElement('canvas');
canvas.classList.add('incomingPhoto');
screenAndroidImage.insertBefore(canvas, screenAndroidImage.firstChild); // screenAndroidImage is a div

Я запускаю следующий код всякий раз, когда изображение отправляется с Android:

if (data.data.byteLength  || typeof data.data !== 'string') {
      var context = canvas.getContext('2d');
      var img = context.createImageData(300, 150);
      img.data.set(data.data);
      context.putImageData(img, 0, 0);
      trace("Image chunk received");
}

Я вижу данные изображения, полученные как ArrayBuffer{} на веб-консоли. Я не вижу, чтобы что-то отображалось на холсте.


person SamFast    schedule 04.11.2015    source источник
comment
Привет @Sojharo, у меня точно такая же ситуация. Как вы решили проблему?   -  person Gaurav    schedule 16.11.2016
comment
@Gaurav извините за поздний ответ. Я добавил свой ответ ниже. Я решил это. Пожалуйста, дайте мне знать, если это не ясно.   -  person SamFast    schedule 26.12.2016


Ответы (2)


Похоже, SkylinkJS на данный момент не поддерживает двоичные передачи. Я предполагаю, что решение, которое можно сделать, состоит в том, чтобы закодировать байты в закодированную строку Base64 и отправить их как сообщение P2P через Интернет. А со стороны Интернета преобразуйте строку base64 в изображение для записи на холст.

Для Android SDK doc API: Прослушиватель сообщений sendP2PMessage Для API документации Web SDK: incomingMessage

person Let    schedule 04.10.2016

Я нашел ошибку и исправление. Прежде всего, в классе ImageAvailableListener нам нужно изменить его, чтобы он поддерживал, если размер изображения превышает ограничение в байтах канала данных webrtc. Если размер изображения превышает наш предел, мы разбиваем его на более мелкие фрагменты байтов.

private class ImageAvailableListener implements ImageReader.OnImageAvailableListener {
    @Override
    public void onImageAvailable(ImageReader reader) {
        Image image = null;
        FileOutputStream fos = null;
        Bitmap bitmap = null;

        ByteArrayOutputStream stream = null;

        try {
            image = imageReader.acquireLatestImage();
            if (image != null) {
                Image.Plane[] planes = image.getPlanes();
                ByteBuffer buffer = planes[0].getBuffer();
                int pixelStride = planes[0].getPixelStride();
                int rowStride = planes[0].getRowStride();
                int rowPadding = rowStride - pixelStride * projectionDisplayWidth;

                // create bitmap
                bitmap = Bitmap.createBitmap(projectionDisplayWidth + rowPadding / pixelStride,
                        projectionDisplayHeight, Bitmap.Config.ARGB_8888);
                bitmap.copyPixelsFromBuffer(buffer);

                stream = new ByteArrayOutputStream();
                bitmap.compress(Bitmap.CompressFormat.JPEG, 5, stream);

                if(stream.toByteArray().length < 16000){
                    ByteBuffer byteBuffer = ByteBuffer.wrap(stream.toByteArray());
                    DataChannel.Buffer buf = new DataChannel.Buffer(byteBuffer, true);

                    Log.w("CONFERENCE_SCREEN", "Image size less than chunk size condition");

                    client.sendDataChannelMessage(buf);

                    client.sendDataChannelMessage(new DataChannel.Buffer(Utility.toByteBuffer("\n"), false));
                } else {
                    // todo break files in pieces here

                    ByteBuffer byteBuffer = ByteBuffer.wrap(stream.toByteArray());
                    DataChannel.Buffer buf = new DataChannel.Buffer(byteBuffer, true);
                    client.sendDataChannelMessage(buf);
                    client.sendDataChannelMessage(new DataChannel.Buffer(Utility.toByteBuffer("\n"), false));
                    //   skylinkConnection.sendData(currentRemotePeerId, stream.toByteArray());
                    Log.w("CONFERENCE_SCREEN", "sending screen data to peer :");
                }

                imagesProduced++;
                Log.w("CONFERENCE_SCREEN", "captured image: " + imagesProduced);
            }

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (fos != null) {
                try {
                    fos.close();
                } catch (IOException ioe) {
                    ioe.printStackTrace();
                }
            }

            if (stream != null) {
                try {
                    stream.close();
                } catch (IOException ioe) {
                    ioe.printStackTrace();
                }
            }

            if (bitmap != null) {
                bitmap.recycle();
            }

            if (image != null) {
                image.close();
            }
        }
    }
}

Код в Интернете

Следующие переменные должны быть объявлены вне функции, которая прослушивает входящие байты из канала данных.

var buf;
var chunks = []; var count;

Тело функции, которая прослушивает канал данных:

   if (typeof data.data === 'string') {
      buf = new Uint8ClampedArray(parseInt(data.data));
      count = 0;
      chunks = [];
      console.log('Expecting a total of ' + buf.byteLength + ' bytes');
      return;
    }
    var imgdata = new Uint8ClampedArray(data.data);
    console.log('image chunk')
    buf.set(imgdata, count);
    chunks[count] = data.data;
    count += imgdata.byteLength;
    if (count === buf.byteLength) {
      // we're done: all data chunks have been received
      //renderPhoto(buf);
      var builder = new Blob(chunks, buf.type);
      console.log('full image received');
      screenViewer.src = URL.createObjectURL(builder);
    }

Где screenViewer — элемент изображения HTML.

person SamFast    schedule 26.12.2016