Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

Технологии сетевых коммуникаций. Учебно-методическое пособие

.pdf
Скачиваний:
0
Добавлен:
15.08.2026
Размер:
1 Мб
Скачать

ство /dev/dsp не создается, но его также можно использовать в режиме эмуляции через приложение padsp.

Таким образом, в системе Ubuntu для возможности обмена данными через виртуальное устройство /dev/dsp необходимо запускать исполняемый файл через команду padsp ./micrec.bin

Мультимедиа оборудование может работать в нескольких форматах данных, например:

AFMT_S8 - 8 бит на отсчет, число со знаком (char) AFMT_U8 - 8 бит на отсчет, беззнаковое число (unsigned char)

AFMT_S16_LE - 16 бит на отсчет, знаковое, порядок байт в слове little endian AFMT_S16_BE - 16 бит на отсчет, знаковое, порядок байт в слове big endian AFMT_U16_LE - 16 бит на отсчет, беззнаковое, порядок байт в слове little endian AFMT_U16_BE - 16 бит на отсчет, беззнаковое, порядок байт в слове big endian

Подробную информацию о режимах работы звуковой карты можно найти в комментариях в файле sys/soundcard.h .

Вывести информацию в звуковую карту возможно аналогичным способом. В реальных приложениях IP телефонии используются специализированные кодеки сжатия данных с потерями, которые предоставляют возможность получать высокие коэффициенты сжатия, обеспечивая приемлемое качество. В рамках данной лабораторной работы будет использован уже знакомый по предыдущим работам алгоритм LZW. Данный алгоритм обеспечивает сжатие без потерь и не является в полной мере оптимальным решением для сетевого аудиовещания (где используются адаптивные кодеки Speex, Vorbis, Layer 3 и другие аналогичные), однако ключевые моменты организации сетевого обмена одинаковы для алгоритмов сжатия всех типов.

2. Методические указания для выполнения работы

Используйте ваши наработки по предыдущим лабораторным работам.

Изучите код приложения-сервера преподавателя.

Для отладки сохраните поток сервера в файл и направьте его непосредственно в файл-устройство «/dev/dsp» или аналогичное. Для воспроизведения потока устройство «/dev/dsp» должно существовать в системе и быть корректно инициализировано, либо воспользуйтесь утилитой padsp.

81

Прослушать результат или сделать запись с микрофона можно также командами aplay и arecord (например, aplay /dev/urandom будет проигрывать «белый шум»).

Устанавливая ключи командной строки утилиты aplay и arecord, возможно устанавливать различные форматы воспроизведения и записи.

В системах, где вместо alsa микшер установлен Pulse audio (например, все современные версии UBUNTU), можно воспользоваться программой padsp. При этом в вашей прикладной программе необходимо вести обмен с виртуальным устройством /dev/dsp. В этом случае прямое копирование файла на устройство будет выглядеть как

padsp cp ./test.raw /dev/dsp

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

На компьютере преподавателя функционирует приложение-сервер, которое осуществляет параллельное вещание нескольких потоков данных в различных звуковых форматах.

Байты 0-3 – признак типа потока. Байты 3-6 – временная метка Байты 7 – данные.

0

1

2

Т и п

3

4

5

6

7

8

9

 

М

 

е

 

т

 

ка

 

Данные

Вещание осуществляется через систему подписки ZMQ и посредством рассылки широковещательных UDP-пакетов.

Реализованы следующие типы аудиопотоков (указаны метки, которые заполняются в пакет):

R08 – 8 бит, моно, 8000 Гц, без сжатия

Z08 – 8 бит моно 8000 Гц, сжатие LZW

Z16 – 16 бит моно 8000 Гц, сжатие LZW (Little Endian) R16 – 16 бит, моно, 8000 Гц, без сжатия (Little Endian)

Каждый из потоков представлен в виде подписки ZMQ и в виде широковещательной рассылки.

82

Сжатие выполняется при помощи библиотеки zlib (методы compress и uncompress). Как уже было сказано выше, первое четырехбайтовое слово пакета содержит уникальную метку пакета (порядковый номер увеличивается с каждым новым пакетом). При воспроизведении аудиоданных пакета необходимо учитывать данную метку и отбрасывать дублированные пакеты, а также учитывать очередность (в простейшем варианте допускается просто отбрасывать устаревшие пакеты, но для качественного воспроизведения рекомендуется организовать буфер). Временную метку не нужно направлять на устройство «audio» (это не является данными звуковых отсчетов).

Для возможности получения широковещательных UDP пакетов необходимо установить соответствующую опцию гнезда Socket:

int a = SO_BROADCAST;setsockopt(fd , SOL_SOCKET , a , sizeof(a) );

Порядок выполнения работы:

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

создайте файл с текстом программы на Си в любом текстовом редакторе, выполняющем действия, согласно варианту;

откомпилируйте программу при помощи компилятора GNU

GCC:

gcc ./yourprog.c -o ./yourprog.bin –lzmq;

выполните отладку программы;

запротоколируйте сетевой обмен (tcpdump) и приведите его в отчете;

в процессе выполнения работы пользуйтесь справкой man <функция>;

проведите эксперимент с имитацией потери пакетов. Изучите, как сказывается на качестве воспроизведения аудиоданных потеря одного или нескольких пакетов;

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

сформируйте поток аудиоданных в виде RAV-файла. Сожмите указанный поток при помощи специализированного кодека для сжатия звука SPEEX. Используйте утилиту speexenc (speexdec). Оцените

83

коэффициент сжатия. Сравните коэффициент сжатия алгоритмом

LZW;

сделайте выводы.

3. Варианты задания

Вариант 1: разработать программный модуль на языке Си для получения и воспроизведения аудиоданных потока UDP пакетов. Воспроизводить поток с меткой RAW.

Вариант 2: разработать программный модуль на языке Си для получения и воспроизведения аудиоданных потока ZMQ подписки. Воспроизводить поток с меткой RAW.

Вариант 3: разработать программный модуль на языке Си для получения и воспроизведения аудиоданных потока UDP пакетов. Воспроизводить поток с меткой Z08.

Вариант 4: разработать программный модуль на языке Си для получения и воспроизведения аудиоданных потока ZMQ подписки. Воспроизводить поток с меткой Z08.

Вариант 5: разработать программный модуль на языке Си для получения и воспроизведения аудиоданных потока UDP пакетов. Воспроизводить поток с меткой Z16.

Вариант 6: разработать программный модуль на языке Си для получения и воспроизведения аудиоданных потока ZMQ подписки. Воспроизводить поток с меткой Z16.

Вариант 7: разработать программный модуль на языке Си для получения и воспроизведения аудиоданных потока ZMQ подписки. Воспроизводить поток с меткой Z08. Протоколировать факт пропадания (утери) пакета.

4. Содержание отчета

Титульный лист.

Цель работы.

Текст программы на языке Си.

Протоколы обмена данными между клиентом и сервером.

Результаты экспериментов.

Выводы по работе.

84

5. Методическая информация для преподавателя

Для выполнения данной лабораторной работы потребуется произвольная версия операционной системы Линукс. Автор настоящего учебно-методического пособия рекомендует использовать для этой цели версию Ubuntu с длительным сроком поддержки (на момент написания данного пособия актуальными являлись версии 14.04 и 12.04). Компиляцию программы следует производить при помощи компилятора gcc , который обычно входит в базовую поставку операционной системы. Компьютер преподавателя с установленной и запущенной на нем программой, размещенной в приложении, должен быть подключен к сети и находиться в пределах одного физического сегмента с компьютерами студентов. Для выполнения работы потребуются пакеты libzmq-dev liblz-dev . Для работы с аудиопотоками требуется установить утилиту aplay. Воспроизводить аудиопоток можно с помощью наушников, подключенных к аудиокарте компьютера. Возможно потребуется найстройка громкости воспроизведения.

Контрольные вопросы

1.Что изменится, если для доставки мультимедиа данных будет использован алгоритм TCP? Какие преимущества и недостатки будут наиболее заметны в этом случае?

2.Почему использование сжатия без потерь не является оптимальным решением для передачи звуковых данных?

3.Чем обусловлен выбор квантования в 0.2 с?

4.Что такое битрейт и как эта величина влияет на качество обмена мультимедиа данными?

Список литературы

1.Танаев А. http://thelib.ru/books/butanaev_anton/programmirovanie_soketovread.html

2.Шарыгин А. Программирование сокетов http://www.rsdn.ru/article/unix/ sockets.xml

3.LinuxShare http://www.linuxshare.ru/docs/devel/languages/C/tppcu/5.htm

4.Select_tutorial http://amax.h16.ru/docs/select_tut2.html

5.Программирование звука http://www.opennet.ru/base/dev/sound.txt.html

6.Кодек для сжатия речевых данных http://www.speex.org

7.Структура аудиофайла http://audiocoding.ru/

85

Приложение 1

Исходный текст серверной части, расположенной на компьютере преподавателя:

#include <speex/speex.h> #include <stdio.h> #include <sys/types.h> #include <sys/socket.h> #include <stdlib.h> #include <unistd.h> #include <netinet/in.h> #include <termios.h> #include <fcntl.h> #include <string.h> #include <assert.h> #include <zmq.h> #include <zlib.h>

void init_ZMQ(void);

void Send_ZMQ(char *,int *,int,unsigned char); void init_UDP(void);

void Send_UDP(char *,int *,int,unsigned char);

unsigned int Frame_ctr=0; //Главный счетчик кадров 8-битного потока unsigned int Frame_ctr2=0; //Главный счетчик кадров 16-битного потока

void *publisher; // ZMQ void *context;

int udp_socket; // Сокет UDP

struct sockaddr_in addr_UDP_BR; // Адрес для бродкаста в рамках теку-

щего сеанса

/* размер фрейма кодирования - 160 отсчетов */

#define FRAME_SIZE 160 #define FRAME_SIZE_MAX 1500

int main(int argc, char **argv)

{

86

char *inFile; char *inFile2;

FILE *fin;// файлы для ввода данных

FILE *fin2;

char in[FRAME_SIZE*2]; // Т.к. у нас массив данных 8-битное знаковое

число

char Zin[FRAME_SIZE_MAX]; // Т.к. у нас массив данных 8-битное зна-

ковое число

int i, tmp,rc;

inFile = argv[1]; inFile2 = argv[2];

fin = fopen(inFile, "rb"); fin2 = fopen(inFile2, "rb");

if(fin==NULL || fin2==NULL) perror("input voice file error"); /* Инициализируем систему ZMQ */

init_ZMQ();

/* Инициализируем систему UDP */ init_UDP();

fseek(fin,48L,SEEK_SET);// пропустили заголовок RIFF, который созда-

ет Arecord в потоке 8 бит

fseek(fin2,48L,SEEK_SET);// пропустили заголовок RIFF, который созда-

ет Arecord в потоке 16 бит

while(1) // общий цикл для вещания всех потоков

{

while (1)

{

/*Читаем аудиофайл ( предполагается стандартный формат 8 бит на отсчет 8000 Гц 1 канал)*/

if( fread(in, 1, FRAME_SIZE, fin)==0) perror("read voice file");; // за-

брали 160 отсчетов

if( feof(fin) ) // Достигли конца файла= пауза 5 секунд тишины

{

printf("FULL LOOP1 %d\n\r", Frame_ctr); fseek(fin,48L,SEEK_SET); // установили указатель чтения на

начало данных

87

break; // перешли к вещанию другого потока

}

Send_UDP((char *)in,&Frame_ctr,FRAME_SIZE,0);// Отправили фрейм тип RA8 в UDP

Send_ZMQ((char *)in,&Frame_ctr,FRAME_SIZE,0);// Отправили фрейм тип RA8 в ZMQ

unsigned long Zlen=FRAME_SIZE*2; // максимальный розмер буфе-

ра, чтобы не случилось переполнение

rc=compress((Bytef *)Zin,&Zlen,( Bytef *)in,FRAME_SIZE); if(rc!=Z_OK) perror("zlib");

Send_UDP((char *)Zin,&Frame_ctr,Zlen,1);// Отправили фрейм тип ZA8 в UDP

Send_ZMQ((char *)Zin,&Frame_ctr,Zlen,1);// Отправили фрейм тип Z08 в ZMQ

Frame_ctr++; usleep(10000); // пауза

}

while (1)

{

//Читаем аудиофайл (предполагается стандартный формат 8 бит на отсчет 8000 Гц 1 канал)

if( fread(in, 2, FRAME_SIZE, fin2)==0) perror("read voice file 2");; //

забрали 160 отсчетов

if( feof(fin2) ) // Достигли конца файла= пауза 5 секунд тишины

{

printf("FULL LOOP %d\n\r", Frame_ctr2); fseek(fin2,48L,SEEK_SET); // установили указатель чтения на

начало данных

break; // перешли к вещанию другого потока

}

Send_UDP((char *)in,&Frame_ctr2,FRAME_SIZE*2,2);// Отправили фрейм тип R16 в UDP

Send_ZMQ((char *)in,&Frame_ctr2,FRAME_SIZE*2,2);// Отправили фрейм тип R16 в ZMQ

unsigned long Zlen=FRAME_SIZE_MAX; // максимальный розмер бу-

фера, чтобы не случилось переполнение

88

rc=compress((Bytef *)Zin,&Zlen,(Bytef *)in,FRAME_SIZE*2); if(rc!=Z_OK) perror("zlib");

Send_UDP((char *)Zin,&Frame_ctr2,Zlen,3);// Отправили фрейм тип Z16 в UDP

Send_ZMQ((char *)Zin,&Frame_ctr2,Zlen,3);// Отправили фрейм тип Z16 в ZMQ

Frame_ctr2++; usleep(10000); // пауза

}

sleep(10);//10 секунд между потоками

}

fclose(fin);fclose(fin2); return 0;

}

void init_UDP(void) // Инициализируем систему рассылки UDP

{

int rc;

int aparam=1;

struct sockaddr_in addr; // структура с типом адресов

udp_socket = socket(AF_INET, SOCK_DGRAM, 17); // инициализировали

сокет

if(udp_socket < 0)

{

perror("socket");

exit(1);

}

addr.sin_family = AF_INET;

addr.sin_port = htons(12345); // Будем вещать с порта 12345 для опре-

леленности

addr.sin_addr.s_addr = htonl(INADDR_ANY); // привязали сокет

if(bind(udp_socket, (struct sockaddr *)&addr, sizeof(addr)) < 0)

{

perror("bind");

exit(2);

}

rc=setsockopt(udp_socket,SOL_SOCKET,SO_BROADCAST,&aparam,sizeof (aparam)); // Включили широковещательный режим

89

if(rc==-1)

{

perror("UDP_PARAM BRODCAST");exit(0);

}

addr_UDP_BR.sin_family = AF_INET; addr_UDP_BR.sin_port = htons(3300); addr_UDP_BR.sin_addr.s_addr = INADDR_BROADCAST;

}

void init_ZMQ(void) // Инициализируем систему рассылки ZMQ

{

context=zmq_init (2); if(context==NULL) exit(0); // создали контекст publisher = zmq_socket (context, ZMQ_PUB);assert (context); // создали

0mq гнездоpublisher zmq_bind(publisher, "tcp://*:5572");

}

void Send_ZMQ(char *message,int *ctr,int len,unsigned char flag) // Отправ-

ка пакета через ZMQ.

{

zmq_msg_t msg; int rc;

unsigned char buf[1500-46-20-4]; // буфер не больше MTU-заголовок

ETHзаголовок IP - признак канала= 1434 if(len>1434) return;// ошибочный пакет

// Формируем структуру пакета

if(flag==0) memcpy(buf,"R08",3);// скопировали 3 байт признака канала if(flag==1) memcpy(buf,"Z08",3);// скопировали 3 байт признака канала if(flag==2) memcpy(buf,"R16",3);// скопировали 3 байт признака канала if(flag==3) memcpy(buf,"Z16",3);// скопировали 3 байт признака канала

memcpy(buf+3,ctr,4);// Заполнили метку времени memcpy(buf+7,message,len);// Заполнили содерджимое пакета;

90

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]