2014-02-05 6 views
5

Я пытаюсь отправить данные изображения по Data Channel, но он не работает. Когда вы просто получаете данные от ctx.getImageData, я получаю строку "[Object ImageData]" с другой стороны. Преобразование части данных только в blob приводит к ошибке: Uncaught NetworkError: Failed to execute 'send' on 'RTCDataChannel': Could not send data. Я получаю ту же ошибку при попытке конвертировать ее в ArrayBuffer. Как мне это сделать?Отправить данные изображения по каналу данных RTC

ответ

9

Вот демо я написал только сейчас: http://richard.to/projects/datachannel-demo/

Обратите внимание, что я использую местные каналы, и я просто отображение изображения и не оказывающее на холст. Это должно быть легко сделать. Вы можете столкнуться с проблемами при фактическом общении с удаленным устройством. Я еще не проверял его. Также он работает только в Chrome. Но должно быть просто сделать работу в Firefox.


Это было немного сложно понять, поскольку материал WebRTC постоянно меняется. Не говоря уже о том, что Firefox и Chrome работают несколько иначе.

Я собираюсь сосредоточиться на Chrome, поскольку сообщения об ошибках, которые вы получаете, относятся к Chrome, в частности Uncaught NetworkError: Failed to execute 'send' on 'RTCDataChannel': Could not send data. Эта проблема была описана здесь: https://groups.google.com/forum/#!topic/discuss-webrtc/U927CZaCdKU

Это связано с ограничением скорости передачи данных RTP data channel. Ссылка, которую я дал вам, упоминал 3 KB/sec и в моем тестировании, который звучит правильно.

Хорошей новостью является то, что после Chrome 31 вы можете использовать каналы передачи данных на основе SCTP. См. Здесь: https://groups.google.com/forum/#!topic/discuss-webrtc/y2A97iCByTU.

Это означает, что вместо этого:

window.localPeerConnection = new webkitRTCPeerConnection(servers, 
    {optional: [{RtpDataChannels: true}]}); 

Вы можете сделать что-то вроде этого (вероятно, можно удалить второй параметр):

window.localPeerConnection = new webkitRTCPeerConnection(servers, 
    {optional: []}); 

Я считаю, вы по-прежнему будет скорость ограничена, но теперь это 64kbps. Возможно, я ошибаюсь в этом номере. Не могу найти ссылку, с которой я ее прочитал.

Одна хорошая вещь в канале SCTP заключается в том, что вместо ненадежного (UDP) вы можете использовать надежное соединение для передачи данных (TCP), и данные отправляются по порядку. Я не уверен в этом. Еще раз, не могу найти ссылку.

Теперь, из-за этого, похоже, вам все равно придется вырезать данные. Вы не можете отправить все это одновременно в Chrome. Однако вы можете сделать это в Firefox.

Второе, что вам нужно знать, это то, что в настоящее время данные не поддерживаются данным устройством blob. По крайней мере, в обычном Chrome 32. Это означает, что мы должны отправлять данные в виде текста, если мы хотим использовать Chrome.

Так что мы можем сделать, это преобразовать наши данные изображения в base64 с canvas.toDataURL().Вот пример того, как это будет работать:

var canvas = document.createElement('canvas'); 
canvas.width = startimage.width; 
canvas.height = startimage.height; 
var ctx = canvas.getContext('2d'); 
ctx.drawImage(startimage, 0, 0, startimage.width, startimage.height); 
var data = canvas.toDataURL("image/jpeg"); 

Теперь, когда у нас есть данные, нам просто нужно разбить bas64 строку:

Вот реализация комков данных, которые я использую в моя демка выше:

function sendData() { 
    trace("Sending data"); 
    sendButton.disabled = true; 
    var canvas = document.createElement('canvas'); 
    canvas.width = startimage.width; 
    canvas.height = startimage.height; 
    var ctx = canvas.getContext('2d'); 
    ctx.drawImage(startimage, 0, 0, startimage.width, startimage.height); 

    var delay = 10; 
    var charSlice = 10000; 
    var terminator = "\n"; 
    var data = canvas.toDataURL("image/jpeg"); 
    var dataSent = 0; 
    var intervalID = 0; 

    intervalID = setInterval(function(){ 
    var slideEndIndex = dataSent + charSlice; 
    if (slideEndIndex > data.length) { 
     slideEndIndex = data.length; 
    } 
    sendChannel.send(data.slice(dataSent, slideEndIndex)); 
    dataSent = slideEndIndex; 
    if (dataSent + 1 >= data.length) { 
     trace("All data chunks sent."); 
     sendChannel.send("\n"); 
     clearInterval(intervalID); 
    } 
    }, delay); 
} 

реализация довольно проста, в основном только с помощью setInterval. Вы можете возиться с параметрами размера и задержки фрагмента. Также нам нужно установить символ терминатора, чтобы узнать, когда сообщение завершено. Я просто использовал символ \n.

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

function handleMessage(event) { 
    if (event.data == "\n") { 
    endimage.src = imageData; 
    trace("Received all data. Setting image."); 
    } else { 
    imageData += event.data; 
    //trace("Data chunk received"); 
    } 
} 

Надеюсь, это поможет. Было интересно исследовать его. Не совсем уверен, что это будет идеальное решение для отправки изображения через WebRTC. Есть некоторые демонстрации, которые делают передачу файлов P2P и прочее. Думаю, это зависит от вашей цели.

+0

Привет, спасибо за ваш ответ. В настоящее время я делаю что-то совершенно другое (и именно поэтому я забыл об этом вопросе). В настоящее время я работаю с веб-сайтами и отправляет координаты рисованной линии, например, вместо изображения. Я могу по-прежнему взглянуть на это, чтобы уменьшить нагрузку на сервер, теперь я знаю, что данные должны быть разделены. – MarijnS95

+0

FYI, я могу отправить строку в Chrome M40, но всегда не удалось при попытке отправить arraybuffer, я получил: «Не удалось выполнить« отправить »на« RTCDataChannel »: не удалось отправить данные« – Imskull

1

Это исключение вызвано превышением предела пропускной способности, согласованного в сеансе.

При создании ответа на предложение, вы должны изменить объект sessionDescription, который является частью PeerConnection, чтобы изменить параметр b=AS:, который содержит максимальную пропускную способность в кбит/с:

var Bandwidth = 5000; 
sessionDescription.sdp = sessionDescription.sdp.replace(/b=AS:([0-9]+)/g, 'b=AS:'+Bandwidth+'\r\n'); 
alert(JSON.stringify(sessionDescription)); 

По умолчанию в значение 30 кбит/с.

+0

Похоже, у меня нет этой части текста в моем sdp. Может быть, когда его нет, он просто принимает значение по умолчанию 30, и нам просто нужно добавить его вручную в sdp? И я предпочел бы иметь регулярное выражение примерно так: '/ b = AS: ([0-9] +)/g', так как я [не мог заставить вас работать] (http://regexr.com/ 398og). Но если бы это сработало, это было бы здорово, хотя было бы еще лучше, если бы сам браузер обрабатывал bandwith (точно так же, как веб-порты (я не уверен, но похоже, что они есть), поскольку я могу просто выбросить (массивные) количества строк через него без сбоев). – MarijnS95

+0

Не работает для меня, протестирован на Chrome M40. – Imskull

+0

Предлагаемое регулярное выражение для замены 30 на 1000 (если это сработало ...): var new_offer = offer.replace (/ (\ Sb = AS:) ([0-9] +) (\ S) /, "$ 11000 $ 3 «); –

1

Это на самом деле комментарий bornSwift, но у меня недостаточно репутации для комментариев. Итак: когда вы используете канал данных RTP, вы увидите пропускную способность приложения (AS) в пределах sdp (т. Е. B = AS: 30). 30 - значение по умолчанию. Вы можете заменить это на произвольное значение (1638400 работает, но если вы хотите нажать его дальше, вам придется проб и ошибок).

Однако при инициализации канала данных SCTP вы не увидите полосы пропускания AS в вашем sdp. Это просто отлично. Вам не о чем беспокоиться.

Смежные вопросы