Разработка прикладного протокола передачи речи в реальном времени

 

ОГЛАВЛЕНИЕ


Цель курсового проекта

Задание на курсовое проектирование

Вариант задания

Анализ задачи

Снимок экрана работающей программы

Алгоритмы работы функций в виде блок-схемдиаграммы

Листинг программы

Список использованной литературы



Цель курсового проекта


Разработка протокола верхнего уровня согласно требованиям на курсовое проектирование.


Задание на курсовое проектирование


1.Разработать структуру протокола согласно функциям (см. табл.1):

1.1.Определить функции протокола и структуру пакета разрабатываемого протокола;

1.2.Описать поля заголовка разрабатываемого протокола, рассчитать необходимую длину полей заголовка;

.3.Рассчитать необходимую длину буфера на приеме в зависимости от длины пакета и максимально допустимой задержки.

2.Разработать алгоритмы обработки данных на приеме и передаче и представить их блок-схемы.

3.Разработать программную реализацию протокола:

3.1.Описать разработанные функции, их назначение и структуру;

3.2.Скомпилировать файл , протестировать на реальной сети.


Вариант задания


Табл.1

АБтип трафиказначение поля дополнительной информациичисло посылок пакетовдлина поля данных пакета, байтзадержка воспроизведения, пак.23Real-timeСимвольное с названием кодека11286

Анализ задачи


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

Для данного типа трафика хорошо подошёл протокол UDP, не гарантирующий доставки пакетов, но не вносящий дополнительных задержек на повторную передачу, как TCP. Но UDP не решал проблему джиттера. Можно было бы создать новый протокол транспортного уровня специально для передачи трафика реального времени, но многоуровневый подход диктует другое решение. Проблему обеспечения качества обслуживания для трафика реального времени решают специальные протоколы прикладного уровня, такие как RTP и RTCP.

Чтобы передать данные, необходимо упаковать их в пакеты прикладного уровня. В этих пакетах кроме поля с данными, содержащими часть передаваемого файла, должны содержаться дополнительные поля со следующей информацией:

)название кодека

)номер пакета

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

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

Частота дискретизации: 8кГц

Точность отсчетов - 8бит

Число каналов - 1 (моно)

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


Рис.1. Организация джиттер-буфера


Так как для передачи трафика используется протокол транспортного уровня UDP, пакеты могут передаваться по сети от источника к получателю по разным маршрутам. В некоторых случаях это может приводить к тому, что пакеты будут приходить не только через разные промежутки времени, но и в неправильном порядке. Чтобы предусмотреть такие ситуации в структуру пакета нужно ввести поле с номером пакета.

При передаче пакеты нумеруются по заданному модулю, а на приеме анализируется номер принятого пакета. Если приходит пакет, номер которого меньше или равен номеру предыдущего пакета, то такой пакет отбрасывается, иначе - отправляется на воспроизведение.

Помимо этого, в структуру пакета можно ввести и другие дополнительные поля для расширения функциональности протокола. Как уже отмечалось выше, в этом варианте в качестве дополнительного поля используется имя отправителя, которое задается на передающей стороне.

Структура пакета

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


Название кодекаНомер пакетаАудиоданныеРис.1.3 Формат пакета приложения передачи речи в реальном времени


Под название кодека выделяется 8 байт, под номер пакета - 4 байта (одна переменная целого типа (int)). Для поля аудиоданных необходимо выделить 128 байт.

Формат пакета задаётся структурой Sound:


struct Sound

{char CodecName[NAMESIZE];countSend;SoundData[BUFFERLEN];

};


Здесь символьные массивы CodecName и SoundData содержат соответственно информацию полей названия кодека и аудиоданных.

Размер строки названия кодека и аудиоданных задаётся через константы NAMESIZE и BUFFERLEN, что сделано для удобства использования этих параметров в других частях программы.

Целочисленная переменная countSend содержит информацию поля номера пакета.

При размере поля аудиоданных 128 байт при кодировании сигнала 8-битными отсчетами с частотой 8 кГц получаем длительность звучания одного блока аудиоданных = 128*(1/8000) =0.016c = 16 мс.

Зная объем памяти, выделяемый под переменные каждого типа можно определить размер пакета. На переменные типа int выделяется по 4 байта, на переменные типа char - по одному байту. В итоге размер заголовка вместе с полем аудио данных составит: 8*1+4*1+128=140 байт. Размер пакета с учетом заголовков транспортного, сетевого и канального уровней составит:140+42=182 байта.

Размер буфера приемника.

Между записью блока и его отправкой на воспроизведение вносится задержка, величина которой должна обеспечивать подавление эффекта джиттера. В этом случае даже если пришедшие из сети пакеты записываются в буфер через случайные моменты времени, а воспроизводятся через равные, если выбрать размер буфера около 100 мс, то проявление эффекта джиттера маловероятно, а вносимая задержка для вещательного типа трафика не критична.

В этом случае размер джиттер-буфера должен быть 6*8=48 байт (6 ячеек по 16 мс, 96мс). В этом случае суммарная задержка, вносимая пакетизацией и джиттер-буфером, составит 16+16*6=112 мс.


Снимок экрана работающей программы




Алгоритмы работы функций в виде блок-схем


Рис.2. Функция записи принятых аудиоданных в циклический буфер int myAudioInput::In(char* Buf)


Рис.3. Функция создания и настройки сокета void myAudioInput::initSocket()

Рис.4. Функция формирования и передачи пакетов void myAudioInput::SendRecord()



Рис.5. Функция приёма пакетов int myAudioInput::readPendingDatagrams()



Рис.6. Запуск программы


SDL-диаграммы





Листинг программы программы

.h

#ifndef MAINWINDOW_H

#define MAINWINDOW_H


#include <QMainWindow>

#include "myaudioinput.h"Ui {MainWindow;

}

RcvThread: public QThread

{_OBJECT:();ConnectSingleShot();run();*Timer2Play;*ptr_mAudioInput;*StateStoped;*SingleshotActive;


};

MainWindow : public QMainWindow

{_OBJECT:MainWindow(QWidget *parent = 0);

~MainWindow();StateStoped;SingleshotActive;CreateThread();

slots:on_pushButton_clicked();

on_pushButton_2_clicked();

on_DstlineEdit_editingFinished();

:::MainWindow *ui;myinputaudio;mRcvThread;<QHostAddress> *ptrHostlist;

};

#endif // MAINWINDOW_H

//////////////////////////////////////.cpp

#include "mainwindow.h"

#include "ui_mainwindow.h"

::MainWindow(QWidget *parent) :(parent),(new Ui::MainWindow)

{>setupUi(this);= true;>myinputaudio.CodecLineEdit = ui->CodecLineEdit;>myinputaudio.RcvPckLineEdit = ui->RcvPckLineEdit;>myinputaudio.SendPktLineEdit = ui->SndPktlineEdit;>myinputaudio.stateLabel = ui->stateLabel;>mRcvThread.ptr_mAudioInput = &myinputaudio;>myinputaudio.Timer2Play = this->mRcvThread.Timer2Play;>myinputaudio.StateStoped = &StateStoped;>mRcvThread.StateStoped = &StateStoped;>myinputaudio.SingleshotActive = &SingleshotActive;>mRcvThread.SingleshotActive = &SingleshotActive;= false;>myinputaudio.tohost.setAddress(ui->DstlineEdit->text());

}

::~MainWindow()

{.deleteLater();.terminate();.wait(1);ui;

("MainWindow Desctructor done!");

}

MainWindow::on_pushButton_clicked()

{(StateStoped==false) return;(myinputaudio.tohost.isNull())

{ui->stateLabel->setText("Ошибка! Введите адрес приемника!");;}>stateLabel->clear();.ConnectSingleShot();.Record();= false;


}

MainWindow::on_pushButton_2_clicked()

{(StateStoped==true) return;.closeSocket();

}

MainWindow::on_DstlineEdit_editingFinished()

{(StateStoped) myinputaudio.tohost.setAddress(ui->DstlineEdit->text());

}

MainWindow::CreateThread()

{.start();

}


::RcvThread()

{Play = new QTimer;Play->setTimerType(Qt::PreciseTimer);

}RcvThread::run()

{*ptrStateStoped = StateStoped;(1)(*ptrStateStoped == false)_mAudioInput->readPendingDatagrams();

}

RcvThread::ConnectSingleShot()

{(Timer2Play,SIGNAL(timeout()),>ptr_mAudioInput,SLOT(startTimer2()));

}

////////////////////////////////////

.h

#ifndef MYAUDIOINPUT_H

#define MYAUDIOINPUT_H


#include <QObject>


#include <QAudioInput>

#include <QAudioOutput>

#include <QDebug>

#include <QFile>

#include <QTimer>

#include <QBuffer>

#include <QUdpSocket>

#include <QtEndian>

#include <QIODevice>

#include <QList>

#include <QThread>

#include <QNetworkInterface>

#include "ui_mainwindow.h"


#define STARTTIMER2 1

#define CODECNAME "PCM"

#define NAMESIZE 8

#define SIZESOUND 140


#define JITTERLEN 6

#define PKTDELAY 16 //delay between sending packets in ms milliseconds

#define FREQPCM 0.000125 //frequncy of pcm codec // 1/8000

#define BUFFERLEN 128 // size of jitterbuffer in bytes , i choose 128byte // (PKTDELAY/FREQPCM)/1000

#define PLAYDELAY 80 //delay between playing rcvd packet in ms // PKTDELAY*JITTERLEN-PKTDELAY

// считается 96-16 = 80, так как мы используем два таймера. один запускает 80мс

// после чего запускается основной таймер2 на 16мс. так как воспроизводим только после 2 таймера,

struct Sound

{CodecName[NAMESIZE];countSend;SoundData[BUFFERLEN];

};myAudioInput : public QObject

{_OBJECT:myAudioInput(QObject *parent = 0);

~myAudioInput();Record();closeSocket();tohost;*StateStoped;*SingleshotActive;*CodecLineEdit;*RcvPckLineEdit;*SendPktLineEdit;*stateLabel;*audio; // need to cut to audioOutput class*Timer2Play;

readPendingDatagrams();

:

slots:handleStateChanged(QAudio::State newState);stopRecording();notified();SendRecord(); //made it publicplaySoundPkt(); //play sound block from jitterbufferOutnotified();InitPlay(); //need to cut to audiOutput classOuthandleStateChanged(QAudio::State newState);startTimer2();:format; //need to copy yo audioOutput class*myaudio;*m_input;*myrecord;mSound; //my protocol packet*audiobuf;countSend;*Timer1Snd;byteready;

*out_input;* ptrSound;*m_udpSocket;*datagram; //for receiveng*outarray;PlayBuf[BUFFERLEN];jitterQueue[JITTERLEN][BUFFERLEN];<QByteArray> jitterbuf;countRcv;fst; //pointer to first packet in jitterqueuwlst; //pointer to last packet in jitterqueue:initSocket();slots:socketStateHandle(QAbstractSocket::SocketState);In(char *Buf);

};

#endif // MYAUDIOINPUT_H

//////////////////////////////////////.cpp

#include "myaudioinput.h"

::myAudioInput(QObject *parent) :(parent)

{= new QByteArray;>resize(SIZESOUND);_input = 0; // iodevice initialize 0_udpSocket = new QUdpSocket(this);Snd = new QTimer;Snd->setTimerType(Qt::PreciseTimer);= 0;= 0;(int i=0;i<NAMESIZE;i++).CodecName[i] = 0;(int i=0;i<JITTERLEN;i++)(int j=0;j<BUFFERLEN;j++)

{ jitterQueue[i][j]=0;.SoundData[j]=0;

}

(mSound.CodecName,CODECNAME);= &mSound;(Timer1Snd,SIGNAL(timeout()),this,SLOT(SendRecord());(m_udpSocket,SIGNAL(stateChanged(QAbstractSocket::SocketState)),,SLOT(socketStateHandle(QAbstractSocket::SocketState)));

// Set up the desired format, for example:.setSampleRate(8000);.setChannelCount(1);.setSampleSize(8);.setCodec("audio/pcm");.setByteOrder(QAudioFormat::BigEndian);.setSampleType(QAudioFormat::UnSignedInt);

info = QAudioDeviceInfo::defaultInputDevice();(!info.isFormatSupported(format)) {() << "Default format not supported, trying to use the nearest.";= info.nearestFormat(format);

}()<<"device input name"<<info.deviceName();= new QAudioInput(format, this);(myaudio,SIGNAL(notify()),this,SLOT(notified())); //stat(myaudio, SIGNAL(stateChanged(QAudio::State)), this, SLOT(handleStateChanged(QAudio::State)));()<<"NotifyInterval ="<<myaudio->notifyInterval();()<<"Supported codecs::"<<info.supportedCodecs();>InitPlay();()<<"BUFFERSIZE of transmitter = "<<myaudio->bufferSize();()<<"Transmitter configured";


}


/*

* Functions that need cut to Output class

*/myAudioInput::InitPlay()

{= new QByteArray;_input = 0; //for playinginfo(QAudioDeviceInfo::defaultOutputDevice());(!info.isFormatSupported(format)) {() << "Raw audio format not supported by backend, cannot play audio.";;

}()<<"device output name"<<info.deviceName();= new QAudioOutput(format, this);(audio,SIGNAL(notify()),this,SLOT(Outnotified()));(audio, SIGNAL(stateChanged(QAudio::State)), this, SLOT(OuthandleStateChanged(QAudio::State)));()<<"BUFFERSIZE of receiver = "<<audio->bufferSize();()<<"Receiver configured";

}

myAudioInput::OuthandleStateChanged(QAudio::State newState)

{(newState) {QAudio::IdleState:

// Finished playing (no more data)

*StateStoped = false;

//qDebug("Playing done!");;

QAudio::StoppedState:

// Stopped for other reasons(audio->error() != QAudio::NoError) {

// Error handling

}*StateStoped = true;;

QAudio::ActiveState:

*StateStoped = false;;QAudio::SuspendedState:("The audio device is in a suspended state, this state will only be entered after suspend() is called.");;

:

// ... other cases as appropriate;

}

}

myAudioInput::Outnotified()

{()<<"output state is"<<audio->state();()<<"buffersize ="<<audio->bufferSize();() << "OutbytesFree = " << audio->bytesFree()

<< ", " << "OutelapsedUSecs = " << audio->elapsedUSecs()

<< ", " << "OutprocessedUSecs = "<< audio->processedUSecs();

}

::~myAudioInput()

{>stop();>disconnect();>stop();>disconnect();myaudio;=NULL;myrecord;datagram;=NULL;outarray;=NULL;Timer1Snd;_udpSocket->disconnect();_udpSocket->disconnectFromHost();_udpSocket->close();m_udpSocket;_udpSocket=NULL;("MyAudioInput Destructor done");

}

myAudioInput::Record()

{(myaudio->state()!=QAudio::StoppedState) return; //check that audio is stopped=1;= 0;>initSocket();_input = audio->start(); //start receive and playing packets_input = myaudio->start(); //start transmitter and recordingSnd->start(16); //timer for 32ms for reading from microphone and sending("Started connection!");

}myAudioInput::handleStateChanged(QAudio::State newState)

{(newState) {QAudio::StoppedState:(myaudio->error() != QAudio::NoError) {("Error in recording!");

// Error handling

} else {("Record finished");

};

QAudio::ActiveState:

;

:;

}

}myAudioInput::stopRecording()

{>stop();

}

myAudioInput::notified()

{

() << "bytesReady = " << myaudio->bytesReady()

<< ", " << "elapsedUSecs = " <<myaudio->elapsedUSecs()

<< ", " << "processedUSecs = "<<myaudio->processedUSecs();()<<"fst="<<fst<<" lst="<<lst<<"rcv ="<<countRcv<<" snd="<<countSend;>CodecLineEdit->setText(ptrSound->CodecName);>RcvPckLineEdit->setText(QString::number(countRcv));>SendPktLineEdit->setText(QString::number(countSend));

}

myAudioInput::initSocket()

{_udpSocket->bind(7755); //listen in any interface("bind");


}

myAudioInput::SendRecord()

{l;= myaudio->bytesReady(); //check for debug, how much bytes is ready to read= m_input->read(mSound.SoundData, BUFFERLEN); //send it like len(l <=0) return;

//fill Sound struct header and payload.countSend = countSend;bufdata((char*)&mSound,SIZESOUND); //((char*)&mSound,SIZESOUND);++; //increase count of Senden packets_udpSocket->writeDatagram(bufdata,bufdata.size(),tohost,7755); //send len + num of packet(1byte)

//

}

myAudioInput::readPendingDatagrams()

{l;sender; //need for debug, or for NAT bypasssenderPort; //need for debug, or for NAT bypass= m_udpSocket->readDatagram(datagram->data(),>size(),&sender, &senderPort);(l<=0) return 0;= (Sound*) datagram->data();prevcount = countRcv;= ptrSound->countSend;(countRcv>prevcount)In(ptrSound->SoundData);return 0;

}

myAudioInput::closeSocket()

{(myaudio->state()==QAudio::StoppedState) return; //check that audio is not stopped

*StateStoped=true;>stop();>stop();Snd->stop();Play->stop();Play->disconnect();_udpSocket->abort();("Socket disconnected");("Recording and playing stopped");


}

myAudioInput:: socketStateHandle(QAbstractSocket::SocketState newState)

{()<<"ATTENTION: socket changed state:"<<newState;

}

myAudioInput::In(char* Buf)

{(jitterQueue[lst],Buf,BUFFERLEN);= (lst+1)%JITTERLEN;(!Timer2Play->isActive() && *SingleshotActive==false)

{Play->setSingleShot(true);Play->start(PLAYDELAY);

*SingleshotActive = true;STARTTIMER2;

}return 0;

}

myAudioInput::playSoundPkt()

{(PlayBuf,jitterQueue[(fst+1)%JITTERLEN],BUFFERLEN);= (fst+1) %JITTERLEN;l= out_input->write(PlayBuf,BUFFERLEN);

}myAudioInput::startTimer2()

{Play->disconnect();

*SingleshotActive = false;Play->setSingleShot(false);(Timer2Play,SIGNAL(timeout()),this,SLOT(playSoundPkt()));Play->start(PKTDELAY);

}

///////////////////////////////////////

.cpp

#include "mainwindow.h"

#include <QApplication>

#include <myaudioinput.h>

main(int argc, char *argv[])

{a(argc, argv);w;.show();.CreateThread();a.exec();

}

протокол буфер прием передача


Список использованной литературы


1.Симонина О. А., Глазунов А. С., Четвертухин В. Г. Сети ЭВМ и телекоммуникации: методические указания к курсовому проектированию (спец. 202000, 220400) / СПбГУТ. СПб, 2004.

.Остерлох, Хизер. TCP/IP. Семейство протоколов передачи данных в сетях компьютеров / Пер. с англ. М.: ДиаСофтЮП, 2002.

3.Камер, Дуглас Э. Сети TCP/IP. Принципы, протоколы и структура / Пер. с англ. М.: Издательский дом «Вильямс», 2003. Т.1.

.Олифер В. Г., Олифер Н. А. Компьютерные сети. Принципы, технологии, протоколы. СПб: Питер, 2002.

.Гольдштейн Б.С., Пинчук А.В., Суховицкий А.Л. IP-Телефония. М.: Радио и связь, 2001.

.Пол А. Объектно-ориентированное программирование на C++ /Пер с англ СПб: Невский диалект - Издательство БИНОМ, 1999.

.Штерн В. Основы C++: Методы программной инженерии. М.: Лори, 2003.


ОГЛАВЛЕНИЕ Цель курсового проекта Задание на курсовое проектирование Вариант задания Анализ задачи Снимок экрана работающей программы Алго

Больше работ по теме:

КОНТАКТНЫЙ EMAIL: [email protected]

Скачать реферат © 2017 | Пользовательское соглашение

Скачать      Реферат

ПРОФЕССИОНАЛЬНАЯ ПОМОЩЬ СТУДЕНТАМ