#c_sharp #net
Есть 2 компьютера, назовем их Компьютер А и Компьютер B.
Мне нужно передать звуковой файл формата PCM с Компьютера А на Компьютер В, отследить
насколько точно передался файл и воспроизвести то, что прислал на Компьютер В на компьютере В.
Чтобы передать файл, использую
socket.SendTo(packet,0,count,SocketFlags.None,remoteEP);
из библиотеки System.Net.Sockets.
В итоге получается, что все передается точно - я отслеживаю программой WireShark
на Компьютере А и на Компьютере В. Однако то, что приходит на компьютер В, полностью
не совпадает с исходным файлом, который передавался.
Программа, которая передает данные файла, открывает этот файл правильно, выставляет
на передачу (Socket.SendTo) именно те байты, которые в исходном файле, но на выходе
Компьютера А WireShark показывает совсем не то, то есть передается неправильно.
В чем может быть проблема?
UPD: Нет, файл передается в течение минуты по таймеру. Передаю по 400 байт.
Изначально в программе передавался звук с микрофона, как только буфер в 400 байт
заполнялся, данные кодировались с помощью кодека G711 - в результате чего сжимались
в 2 раза и передавались. То есть 200 байт. Надо будет попробовать передавать меньшее
количество байт, может в этом проблема.
В общем, я протестировал передачу данных. Взял массив из 10 байт и передал.
byte[] array = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a };
m_pUdpServer.SendPacket(array, 0, 10, m_pTargetEP);
В итоге, на выходе компьютера А те же самые байты (0-10). Все правильно передается.
Или в цикле:
byte[] array = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a };
while(true)
m_pUdpServer.SendPacket(array, 0, 10, m_pTargetEP);
Тоже все передается без ошибок.
А вот код, который я использую для передачи звукового файла по таймеру.
private void m_pTimer_Tick(object sender,EventArgs e)
{
uint sent_data = 0;
while ((sent_data <= (BUFFERSIZE / 120)) && ((num * RAW_PACKET) + sent_data
< BUFFERSIZE))
{
uint bytes_count = ((BUFFERSIZE - (RAW_PACKET * num)) > RAW_PACKET) ?
RAW_PACKET : (BUFFERSIZE - (RAW_PACKET * num));
byte[] buffer = new byte[bytes_count];
Array.Copy(ReadBuffer, num * RAW_PACKET, buffer, 0, bytes_count);
num++;
// Send and read next.
m_pUdpServer.SendPacket(buffer, 0, Convert.ToInt32(bytes_count), m_pTargetEP);
sent_data += bytes_count;
}
if ((num * RAW_PACKET) + sent_data == BUFFERSIZE)
{
m_pTimer.Enabled = false;
}
m_pPacketsReceived.Text = m_pUdpServer.PacketsReceived.ToString();
m_pBytesReceived.Text = m_pUdpServer.BytesReceived.ToString();
m_pPacketsSent.Text = m_pUdpServer.PacketsSent.ToString();
m_pBytesSent.Text = m_pUdpServer.BytesSent.ToString();
}**
Здесь 120 - количество частей файла, которые передаются в течение каждого вызова
таймера. Файл длится 1 минуту. То есть, таймер вызывается 2 раза в секунду. Каждый
раз, когда он вызывается, ему нужно передать (BUFFERSIZE / 120) файла, где BUFFERSIZE
- общий размер файла. И он в цикле передает эту часть файла по RAW_PACKET(RAW_PACKET
= 400 байт).
sent_data - общее количество байт, переданных в течение каждого вызова таймера.
num - общее количество отправленных пакетов.
ReadBuffer - общий массив, в котором хранятся все байты файла.
Я поставил точку останова на строку
m_pUdpServer.SendPacket(buffer, 0, Convert.ToInt32(bytes_count), m_pTargetEP);
Программа зашла в нее, посмотрел значение переменных, элементов массива и запустил
программу далее. И так несколько раз. То есть я вызывал строку
m_pUdpServer.SendPacket(buffer, 0, Convert.ToInt32(bytes_count), m_pTargetEP);
несколько раз с задержкой в несколько секунд.
И получилось, что все передается правильно.
А когда я просто запускаю таймер, который без перерыва передает данные в цикле -
получаются неверные результаты.
Сам файл достаточно большой - около 50 Мб, то есть в секунду надо передать около
800 000 байт.
То есть, получается, что код программы в функции таймера не справляется с требуемой
нагрузкой и поэтому происходит неверная передача байтов?
Ответы
Ответ 1
Причина в том что когда срабатывает таймер в "плавание" почти одновременно уходит множество пакетов, очередность и доставка не гарантируется. По этому на выходе получаем 800 кб перемешаных данных. Чтобы хоть как-то заработало нужно добавить идентификатор пакета и на принимающей стороне предусмотреть склеивание пакетов в правильном порядке и пропуск пакетов которые за некоторое время так и не дошли. Также очень рекомендую избавится от таймера и отправлять данные равномерно, синхронизируясь по времени. Также придется добавить контрольные суммы. Под "синхронизируясь по времении" я имел ввиду поток который спит пока мы опережаем график и отправляет данные если настало время или мы отстаем. Определить график отправки не сложно, так как известен битрейт. Еще раз повторюсь и посоветую посмотреть в сторону стандартных решений.Ответ 2
Нечего переизобретать RTP. Этот протокол специально предназначен для передачи медиаданных в сетях с негарантированной доставкой и нестабильностью скорости. Так что берите готовый протокол и не теряйте время.
Комментариев нет:
Отправить комментарий