Как динамически установить метки верхнего и нижнего уровня DataChannel для регулирования - PullRequest
0 голосов
/ 24 октября 2019

Мое приложение Android отправляет большие файлы (может быть 100 МБ) между двумя пирами, используя WebRTC. Когда я изначально писал код, который передает данные между пирами, используя DataChannel, я просто отправлял куски ByteBuffer так быстро, как мог. Я быстро обнаружил, что при этом переполняет внутренний буфер в DataChannel. Итак, я понимаю, что мне нужно реализовать «политику регулирования».

Я предпочитаю, чтобы я мог динамически регулировать скорость в зависимости от устройств в игре и текущих сетевых условиях. Но у меня есть фундаментальные вопросы о том, как это сделать.

В настоящее время я использую подход статического газа, как это:

// Settings for DataChannel
private static final int DATACHANNEL_BUFFER_HIGH_WATER_MARK = (1024*512); // pause when buffer gets to half MB
private static final int DATACHANNEL_BUFFER_LOW_WATER_MARK = (1024*256); // resume when buffer gets down to 256k

И мой код передачи выглядит так:

            // Open file for reading, and send chunks via the data channel
            int bytesRead = 0;
            boolean keepGoin = true;
            byte[] fileChunk = new byte[WEBRTC_TRANSFER_BUFFER_SIZE];
            long bytesSent = 0;
            origin = new BufferedInputStream(new FileInputStream(zipFile), WEBRTC_TRANSFER_BUFFER_SIZE);
            while (keepGoin && ((bytesRead = origin.read(fileChunk, 0, WEBRTC_TRANSFER_BUFFER_SIZE)) != -1)) {

                bytesSent += bytesRead;
                ByteBuffer wrap = ByteBuffer.wrap(fileChunk, 0, bytesRead);
                peerDataChannel.send(new DataChannel.Buffer(wrap, false));
                if (peerDataChannel.bufferedAmount() > DATACHANNEL_BUFFER_HIGH_WATER_MARK) {

                    while (keepGoin && peerDataChannel.bufferedAmount() > DATACHANNEL_BUFFER_LOW_WATER_MARK) {
                        Thread.sleep(250);

                    }
                }
            }

Хотя это работает в процессе разработки, я уверен, что в некоторых случаях я «оставляю скорость на столе» (потому что верхняя отметка слишком низкая), а в других я могу себе представить, что на самом деле это все еще может быть переполнением. буфер в канале данных (на устройствах с не такой большой памятью и т. д.).

Вопросы:

  1. Как узнать, какой размер внутреннего буфера по умолчанию используется дляактивный канал данных? Можно ли его отрегулировать?
  2. Является ли подход установления "отметки максимальной воды" и "отметки низкой воды" и позволяет каналу данных пропускать между ними правильный подход?
  3. Является ли целесообразным "Политика корректировки «для поддержания более тесного промежутка между высоким и низким уровнем?»
  4. Существует ли «наилучшая практика» для определения наиболее подходящего уровня максимума / минимума для любой конкретной ситуации?
  5. На принимающей стороне я в настоящее время ставлю в очередь каждый массив byte [], полученный через DataChannel.onMessage () при передаче файла в LinkedBlockingQueue, который является субъектом отдельного потока, который извлекает куски ByteBuffer из очереди и записываетих на диск так быстро, как это возможно. В DataChannel.onMessage () это выглядит так:

                public void onMessage(DataChannel.Buffer buffer) {
                    // Recipient's data channel
                    byte[] bytes;
                    boolean isReceivingFileData = false;
    
                    if (buffer.data.hasArray()) {
                        bytes = buffer.data.array();
                    } else {
                        bytes = new byte[buffer.data.remaining()];
                        buffer.data.get(bytes);
                    }
    
                    // fileTransferQueue starts as null, and is set to not null
                    // when a file transfer starts and back to null when xfer is complete
                    isReceivingFileData = (fileTransferQueue != null);
                    if ( isReceivingFileData ) {
                        try {
                            fileTransferQueue.put(bytes); // Placing bytes in queue for write thread
                        } catch (InterruptedException e) {
                            Timber.e("Lifecycle (t: %d, id: %s): InterruptedException transferring data: %s", Thread.currentThread().getId(),shareId, e.getMessage());
                            //TODO - should we bail or rety or...?
                        }
    

Затем в моем отдельном потоке FileTransferReceiver мы имеем:

private class FileTransferReceiver extends Thread {
    private String fileTransferInfo;
    private long bytesWritten = 0;
    private boolean keepGoin = false;

    FileTransferReceiver(String fileTransferInfo) {
        super("FileTransferReceiver");
        this.fileTransferInfo = fileTransferInfo;
    }

    public void run() {
        BufferedOutputStream bos = null;
        boolean interruptedByException = false;
        String[] fileData = fileTransferInfo.split(BODY_COMPONENTS_SEPARATOR);
        File fileToWrite = new File(getExternalTransferCacheDir(), fileData[BODY_ZIPFILE_NAME]);
        long bytesToWrite = Long.parseLong(fileData[BODY_ZIPFILE_SIZE]);
        int nextMediaIndexToShare = Integer.parseInt(fileData[BODY_MEDIA_INDEX]);
        InTouchMessage shareMessage = new InTouchMessage(shareId,webRTCLocalUserName,webRTCRemoteUserName,"", RECEIVER_SEZ_SENDING_FILE_PROGRESS,null);
        try {
            Timber.d("Lifecycle (t: %d, id: %s): writing transfer file to location: %s", Thread.currentThread().getId(),shareId, fileToWrite.getCanonicalPath());
            keepGoin = true;
            bos = new BufferedOutputStream(new FileOutputStream(fileToWrite));
            int chunkedProgress = 0;
            int numberOfFives = 0;
            while (keepGoin && (bytesWritten < bytesToWrite)) {
                byte[] chunkToWrite = fileTransferQueue.take(); // Grabbing bytes to write from queue
                bos.write(chunkToWrite);
                bytesWritten += chunkToWrite.length;

Это кажется разумнымПодход?

Кстати, я сделал обзор этого поста , который, как кажется, написан для работы браузер-браузер вместо мобильного приложения, имел некоторые интересные идеи. Это где я получил некоторые из моих настроек буфера по умолчанию. Но я все же хотел бы понять из подхода, основанного исключительно на мобильных приложениях, что такое «лучшие практики».

...