Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Курсовая по сетям.docx
Скачиваний:
0
Добавлен:
01.07.2025
Размер:
345.85 Кб
Скачать

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

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

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

Номер пакета

Аудиоданные

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

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

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

struct Sound

{

char CodecName[NAMESIZE];

qint32 countSend;

char 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-диаграммы

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

mainwindow.h

#ifndef MAINWINDOW_H

#define MAINWINDOW_H

#include <QMainWindow>

#include "myaudioinput.h"

namespace Ui {

class MainWindow;

}

class RcvThread: public QThread

{

Q_OBJECT

public:

RcvThread();

void ConnectSingleShot();

void run();

QTimer *Timer2Play;

myAudioInput *ptr_mAudioInput;

bool *StateStoped;

bool *SingleshotActive;

};

class MainWindow : public QMainWindow

{

Q_OBJECT

public:

explicit MainWindow(QWidget *parent = 0);

~MainWindow();

bool StateStoped;

bool SingleshotActive;

void CreateThread();

private slots:

void on_pushButton_clicked();

void on_pushButton_2_clicked();

void on_DstlineEdit_editingFinished();

private:

Ui::MainWindow *ui;

myAudioInput myinputaudio;

RcvThread mRcvThread;

QList<QHostAddress> *ptrHostlist;

};

#endif // MAINWINDOW_H

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

mainwindow.cpp

#include "mainwindow.h"

#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent) :

QMainWindow(parent),

ui(new Ui::MainWindow)

{

ui->setupUi(this);

StateStoped = true;

this->myinputaudio.CodecLineEdit = ui->CodecLineEdit;

this->myinputaudio.RcvPckLineEdit = ui->RcvPckLineEdit;

this->myinputaudio.SendPktLineEdit = ui->SndPktlineEdit;

this->myinputaudio.stateLabel = ui->stateLabel;

this->mRcvThread.ptr_mAudioInput = &myinputaudio;

this->myinputaudio.Timer2Play = this->mRcvThread.Timer2Play;

this->myinputaudio.StateStoped = &StateStoped;

this->mRcvThread.StateStoped = &StateStoped;

this->myinputaudio.SingleshotActive = &SingleshotActive;

this->mRcvThread.SingleshotActive = &SingleshotActive;

SingleshotActive = false;

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

}

MainWindow::~MainWindow()

{

mRcvThread.deleteLater();

mRcvThread.terminate();

mRcvThread.wait(1);

delete ui;

qDebug("MainWindow Desctructor done!");

}

void MainWindow::on_pushButton_clicked()

{

if(StateStoped==false) return;

if (myinputaudio.tohost.isNull())

{ui->stateLabel->setText("Ошибка! Введите адрес приемника!");

return;}

ui->stateLabel->clear();

mRcvThread.ConnectSingleShot();

myinputaudio.Record();

StateStoped = false;

}

void MainWindow::on_pushButton_2_clicked()

{

if (StateStoped==true) return;

myinputaudio.closeSocket();

}

void MainWindow::on_DstlineEdit_editingFinished()

{

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

}

void MainWindow::CreateThread()

{

mRcvThread.start();

}

RcvThread::RcvThread()

{

Timer2Play = new QTimer;

Timer2Play->setTimerType(Qt::PreciseTimer);

}

void RcvThread::run()

{

bool *ptrStateStoped = StateStoped;

while(1)

if (*ptrStateStoped == false)

ptr_mAudioInput->readPendingDatagrams();

}

void RcvThread::ConnectSingleShot()

{

connect(Timer2Play,SIGNAL(timeout()),

this->ptr_mAudioInput,SLOT(startTimer2()));

}

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

myinputaudio.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

{

char CodecName[NAMESIZE];

qint32 countSend;

char SoundData[BUFFERLEN];

};

class myAudioInput : public QObject

{

Q_OBJECT

public:

explicit myAudioInput(QObject *parent = 0);

~myAudioInput();

void Record();

void closeSocket();

QHostAddress tohost;

bool *StateStoped;

bool *SingleshotActive;

QLineEdit *CodecLineEdit;

QLineEdit *RcvPckLineEdit;

QLineEdit *SendPktLineEdit;

QLabel *stateLabel;

QAudioOutput *audio; // need to cut to audioOutput class

QTimer *Timer2Play;

int readPendingDatagrams();

signals:

public slots:

void handleStateChanged(QAudio::State newState);

void stopRecording();

void notified();

void SendRecord(); //made it public

void playSoundPkt(); //play sound block from jitterbuffer

void Outnotified();

void InitPlay(); //need to cut to audiOutput class

void OuthandleStateChanged(QAudio::State newState);

void startTimer2();

private:

QAudioFormat format; //need to copy yo audioOutput class

QAudioInput *myaudio;

QIODevice *m_input;

QByteArray *myrecord;

Sound mSound; //my protocol packet

QBuffer *audiobuf;

qint32 countSend;

QTimer *Timer1Snd;

qint64 byteready;

QIODevice *out_input;

Sound* ptrSound;

QUdpSocket *m_udpSocket;

QByteArray *datagram; //for receiveng

QByteArray *outarray;

char PlayBuf[BUFFERLEN];

char jitterQueue[JITTERLEN][BUFFERLEN];

QList<QByteArray> jitterbuf;

qint32 countRcv;

uchar fst; //pointer to first packet in jitterqueuw

uchar lst; //pointer to last packet in jitterqueue

private:

void initSocket();

private slots:

void socketStateHandle(QAbstractSocket::SocketState);

int In(char *Buf);

};

#endif // MYAUDIOINPUT_H

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

myaudioinput.cpp

#include "myaudioinput.h"

myAudioInput::myAudioInput(QObject *parent) :

QObject(parent)

{

datagram = new QByteArray;

datagram->resize(SIZESOUND);

m_input = 0; // iodevice initialize 0

m_udpSocket = new QUdpSocket(this);

Timer1Snd = new QTimer;

Timer1Snd->setTimerType(Qt::PreciseTimer);

fst = 0;

lst = 0;

for(int i=0;i<NAMESIZE;i++)

mSound.CodecName[i] = 0;

for (int i=0;i<JITTERLEN;i++)

for(int j=0;j<BUFFERLEN;j++)

{ jitterQueue[i][j]=0;

mSound.SoundData[j]=0;

}

strcpy(mSound.CodecName,CODECNAME);

ptrSound = &mSound;

connect(Timer1Snd,SIGNAL(timeout()),this,SLOT(SendRecord());

connect(m_udpSocket,SIGNAL(stateChanged(QAbstractSocket::SocketState)),

this,SLOT(socketStateHandle(QAbstractSocket::SocketState)));

// Set up the desired format, for example:

format.setSampleRate(8000);

format.setChannelCount(1);

format.setSampleSize(8);

format.setCodec("audio/pcm");

format.setByteOrder(QAudioFormat::BigEndian);

format.setSampleType(QAudioFormat::UnSignedInt);

QAudioDeviceInfo info = QAudioDeviceInfo::defaultInputDevice();

if (!info.isFormatSupported(format)) {

qDebug() << "Default format not supported, trying to use the nearest.";

format = info.nearestFormat(format);

}

qDebug()<<"device input name"<<info.deviceName();

myaudio = new QAudioInput(format, this);

connect(myaudio,SIGNAL(notify()),this,SLOT(notified())); //stat

connect(myaudio, SIGNAL(stateChanged(QAudio::State)), this, SLOT(handleStateChanged(QAudio::State)));

qDebug()<<"NotifyInterval ="<<myaudio->notifyInterval();

qDebug()<<"Supported codecs::"<<info.supportedCodecs();

this->InitPlay();

qDebug()<<"BUFFERSIZE of transmitter = "<<myaudio->bufferSize();

qDebug()<<"Transmitter configured";

}

/*

* Functions that need cut to Output class

*/

void myAudioInput::InitPlay()

{

outarray = new QByteArray;

out_input = 0; //for playing

QAudioDeviceInfo info(QAudioDeviceInfo::defaultOutputDevice());

if (!info.isFormatSupported(format)) {

qWarning() << "Raw audio format not supported by backend, cannot play audio.";

return;

}

qDebug()<<"device output name"<<info.deviceName();

audio = new QAudioOutput(format, this);

connect(audio,SIGNAL(notify()),this,SLOT(Outnotified()));

connect(audio, SIGNAL(stateChanged(QAudio::State)), this, SLOT(OuthandleStateChanged(QAudio::State)));

qDebug()<<"BUFFERSIZE of receiver = "<<audio->bufferSize();

qDebug()<<"Receiver configured";

}

void myAudioInput::OuthandleStateChanged(QAudio::State newState)

{

switch (newState) {

case QAudio::IdleState:

// Finished playing (no more data)

*StateStoped = false;

//qDebug("Playing done!");

break;

case QAudio::StoppedState:

// Stopped for other reasons

if (audio->error() != QAudio::NoError) {

// Error handling

}

else *StateStoped = true;

break;

case QAudio::ActiveState:

*StateStoped = false;

break;

case QAudio::SuspendedState:

qDebug("The audio device is in a suspended state, this state will only be entered after suspend() is called.");

break;

default:

// ... other cases as appropriate

break;

}

}

void myAudioInput::Outnotified()

{

qDebug()<<"output state is"<<audio->state();

qDebug()<<"buffersize ="<<audio->bufferSize();

qDebug() << "OutbytesFree = " << audio->bytesFree()

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

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

}

myAudioInput::~myAudioInput()

{

audio->stop();

audio->disconnect();

myaudio->stop();

myaudio->disconnect();

delete myaudio;

myaudio=NULL;

delete myrecord;

delete datagram;

datagram=NULL;

delete outarray;

outarray=NULL;

delete Timer1Snd;

m_udpSocket->disconnect();

m_udpSocket->disconnectFromHost();

m_udpSocket->close();

delete m_udpSocket;

m_udpSocket=NULL;

qDebug("MyAudioInput Destructor done");

}

void myAudioInput::Record()

{

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

countSend=1;

countRcv = 0;

this->initSocket();

out_input = audio->start(); //start receive and playing packets

m_input = myaudio->start(); //start transmitter and recording

Timer1Snd->start(16); //timer for 32ms for reading from microphone and sending

qDebug("Started connection!");

}

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

{

switch (newState) {

case QAudio::StoppedState:

if (myaudio->error() != QAudio::NoError) {

qDebug("Error in recording!");

// Error handling

} else {

qDebug("Record finished");

}

break;

case QAudio::ActiveState:

break;

default:

break;

}

}

void myAudioInput::stopRecording()

{

myaudio->stop();

}

void myAudioInput::notified()

{

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

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

<< ", " << "processedUSecs = "<<myaudio->processedUSecs();

qDebug()<<"fst="<<fst<<" lst="<<lst<<"rcv ="<<countRcv<<" snd="<<countSend;

this->CodecLineEdit->setText(ptrSound->CodecName);

this->RcvPckLineEdit->setText(QString::number(countRcv));

this->SendPktLineEdit->setText(QString::number(countSend));

}

void myAudioInput::initSocket()

{

m_udpSocket->bind(7755); //listen in any interface

qDebug("bind");

}

void myAudioInput::SendRecord()

{

qint64 l;

byteready = myaudio->bytesReady(); //check for debug, how much bytes is ready to read

l = m_input->read(mSound.SoundData, BUFFERLEN); //send it like len

if (l <=0) return;

//fill Sound struct header and payload

mSound.countSend = countSend;

QByteArray bufdata((char*)&mSound,SIZESOUND); //((char*)&mSound,SIZESOUND);

countSend++; //increase count of Senden packets

m_udpSocket->writeDatagram(bufdata,bufdata.size(),tohost,7755); //send len + num of packet(1byte)

//

}

int myAudioInput::readPendingDatagrams()

{

qint64 l;

QHostAddress sender; //need for debug, or for NAT bypass

quint16 senderPort; //need for debug, or for NAT bypass

l = m_udpSocket->readDatagram(datagram->data(),

datagram->size(),&sender, &senderPort);

if (l<=0) return 0;

ptrSound = (Sound*) datagram->data();

qint32 prevcount = countRcv;

countRcv = ptrSound->countSend;

if (countRcv>prevcount)

return In(ptrSound->SoundData);

else return 0;

}

void myAudioInput::closeSocket()

{

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

*StateStoped=true;

myaudio->stop();

audio->stop();

Timer1Snd->stop();

Timer2Play->stop();

Timer2Play->disconnect();

m_udpSocket->abort();

qDebug("Socket disconnected");

qDebug("Recording and playing stopped");

}

void myAudioInput:: socketStateHandle(QAbstractSocket::SocketState newState)

{

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

}

int myAudioInput::In(char* Buf)

{

memcpy(jitterQueue[lst],Buf,BUFFERLEN);

lst = (lst+1)%JITTERLEN;

if (!Timer2Play->isActive() && *SingleshotActive==false)

{

Timer2Play->setSingleShot(true);

Timer2Play->start(PLAYDELAY);

*SingleshotActive = true;

return STARTTIMER2;

}

else return 0;

}

void myAudioInput::playSoundPkt()

{

memcpy(PlayBuf,jitterQueue[(fst+1)%JITTERLEN],BUFFERLEN);

fst = (fst+1) %JITTERLEN;

qint64 l= out_input->write(PlayBuf,BUFFERLEN);

}

void myAudioInput::startTimer2()

{

Timer2Play->disconnect();

*SingleshotActive = false;

Timer2Play->setSingleShot(false);

connect(Timer2Play,SIGNAL(timeout()),this,SLOT(playSoundPkt()));

Timer2Play->start(PKTDELAY);

}

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

main.cpp

#include "mainwindow.h"

#include <QApplication>

#include <myaudioinput.h>

int main(int argc, char *argv[])

{

QApplication a(argc, argv);

MainWindow w;

w.show();

w.CreateThread();

return a.exec();

}