Модераторы: feodorv
  

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Передача речи по сети! 
:(
    Опции темы
daff
Дата 24.4.2008, 16:25 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 7
Регистрация: 25.3.2008

Репутация: нет
Всего: нет



Люди, помогите пожалуйста решить проблему!
 Необходимо создать приложение, которое вещает речь по сети. С одной стороны человек говорит что-нибудь в микрофон, а с другой принимается и воспроизводится! Я организовал передачу речи, запихивание её в буфер и приём в буфер на другой стороне, но при воспроизведении речи слышны очень сильные щелчки и абсолютно не слышно самой речи - слышно только допустим постукивание по микрофону!В чём может быть проблема?

Код

//подключение необходимых библиотек
#include <vcl.h>
#include <MMSystem.h>
#include <winsock2.h>

//включение информации из файла
#include "voi.h"

#pragma hdrstop
#pragma package(smart_init)
#pragma resource "*.dfm"

TForm1 *Form1;
//---------------------------------------------------------------------------
//необходимые переменные и константы
const int SIZE_OF_VOICE=128;  //размер области аудиоданных пакета

//параметры буфера для принятых пакетов
int fst;  //указатель на первый элемент буфера
int lst;  //указатель на следующий за последним элемент буфера
const int SIZE_OF_BUF=7;  //максимальная длина буфера в пакетах
char QUEUE[SIZE_OF_BUF][SIZE_OF_VOICE];  //буфер

//буфер для воспроизведения
char PlayBuf[SIZE_OF_VOICE];

bool EnableReceive=false;  //индикатор разрешения приёма трансляции
bool StreamOpen=false;  //индикатор создания потока
bool Timer2Enable=false;  //инликатор запуска таймера на воспроизведение
char WaveBuf[SIZE_OF_VOICE];  //буфер для считанных из файла данных
HANDLE Soc_id;  //дескриптор потока
DWORD S_id;  //идентификатор потока

//структура, описывающая буферы для записи и проигрывания звука
WAVEHDR pwhPlay, pwhRec;
HWAVEOUT hwo;  //идентификатор устройства воспроизведения
HWAVEIN phwi;  //идентификатор устройства записи
MMRESULT mres;  //переменная для хранения результата выполнения функции

//структуры с описанием формата звуковых данных
WAVEFORMATEX CurrentFormatFile;

//функция записи принятых аудиоданных в циклический буфер
void In(char* SoundBuff);
//---------------------------------------------------------------------------
//структура пакета
struct Voice
{
        char Name[10]; //имя отправителя
        int Number;  //номер кадра
        char VoiceData[SIZE_OF_VOICE];  //аудиоданные
};
//---------------------------------------------------------------------------
//функция записи принятых аудиоданных в циклический буфер
void In(char* Buf)
{
        //запись в буфер
        memcpy(QUEUE[lst],Buf,SIZE_OF_VOICE);
        lst=(lst+1)%SIZE_OF_BUF;

        //разрешить срабатывание таймера на воспроизведение принятых данных
        //если он еще не запущен
        if (Timer2Enable==false)
        {
                Form1->Timer2->Enabled=true;
                Timer2Enable=true;
        };
}
//---------------------------------------------------------------------------
//описание класса TSoc
class TSoc
{
public:
        int i, j;  //число отправленных/принятых пакетов
        int prevNumber;  //номер предыдущего принятого пакета
        SOCKET s;  //идентификатор сокета
        SOCKADDR_IN to;  //структура с описанием адреса получателя
        SOCKADDR_IN from;  //структура с описанием адреса отправителя
        char RecvBuffer[SIZE_OF_VOICE+14];  //буфер для принятого пакета
        Voice sVoice;  //объект класса Sound

        TSoc();  //конструктор класса
        bool Create();  //создание и инициализация сокета
        bool Receive();  //приём данных из сети
        void Send(char* SendVoiceBuf);  //отправка данных
};
//---------------------------------------------------------------------------
//конструктор класса TSoc
TSoc::TSoc()
{
        i=1;  //число приняных пакетов
        j=1;  //число переданных пакетов
        prevNumber=0;  //номер предыдущего принятого пакета
}
//---------------------------------------------------------------------------
//создание сокета
bool TSoc::Create()
{
        //инициализация сокета
        WSADATA wsadata;
        WORD wVersionRequested;
        wVersionRequested = MAKEWORD( 2, 0 );
        if (WSAStartup(wVersionRequested,&wsadata)==SOCKET_ERROR)
        {
                MessageBox(0,"Error: WSAStart",0,MB_OK);
                return false;
        };

        //инициализация сокета и присвоение идентификатора
        s=socket(PF_INET,SOCK_DGRAM,IPPROTO_UDP);
        if(s==INVALID_SOCKET)
        {
                MessageBox(0,"Error: Can't create socket",0,MB_OK);
                return false;
        };

        //создание структуры адреса откуда будут принимаются пакеты
        from.sin_addr.S_un.S_addr=ADDR_ANY;
        from.sin_family=PF_INET;
        from.sin_port=5000;

        //подключение сокета к коммуникационной среде
        int errSer=bind(s,(LPSOCKADDR)&from,sizeof(from));
        if(errSer!=0)
        {
                MessageBox(0,"Error: Bind",0,MB_OK);
                closesocket(s);  //закрытие сокета
                return false;
        };

        return true;
}
//---------------------------------------------------------------------------
//передача звука по сети
void TSoc::Send(char* SendVoiceBuf)
{
        //заполнение заголовка пакета
        //имя отправителя берётся из поля От кого
        memcpy(sVoice.Name,Form1->From->Text.c_str(),10);
        //номер пакета
        sVoice.Number=j;

        //заполнение поля данных
        memcpy(sVoice.VoiceData,SendVoiceBuf,SIZE_OF_VOICE);

        //формирование структуры с IP-адресом отправки
        to.sin_family=PF_INET;
        to.sin_addr.S_un.S_addr=inet_addr(Form1->To->Text.c_str());
        to.sin_port=5000;

        //отправка пакета
        int err=sendto(s,(char*)(&sVoice),sizeof(Voice),0,(struct sockaddr*)&to,sizeof(to));
        if (err==SOCKET_ERROR) MessageBox(0,"Error: Send sound",0,MB_OK);
        //число переданных пакетов
        else
        {
                Form1->lSend->Caption="Послано пакетов: "+IntToStr(j);
                j++;
        };
}
//---------------------------------------------------------------------------
//приём данных из сети
bool TSoc::Receive()
{
        //определение наличия пришедших данных
        unsigned long lSize;
        ioctlsocket(s,FIONREAD,&lSize);
        if (lSize==0) return false;

        //приём данных
        int cadr=sizeof(from);
        recvfrom(s,RecvBuffer,SIZE_OF_VOICE,0,(struct sockaddr*)&from,&cadr);

        //указатель на буфер с принятыми данными
        Voice* pointVoice;
        pointVoice=(Voice*) RecvBuffer;

        //анализ номера пакета
        //если нумерация нарушается, то пакет не воспроизводится
        if (pointVoice->Number>prevNumber)
        {
                //вывод числа принятых пакетов и имени отправителя
                AnsiString st=pointVoice->Name;
                Form1->lReceive->Caption="От "+st+" принято пакетов: "+IntToStr(i);
                i++;

                //отправка данных в буфер для накопления
                In(pointVoice->VoiceData);
        };
        return true;
}

TSoc Soc;  //объект класса TSoc
//---------------------------------------------------------------------------
//функция потока для контроля данных пришедших на сокет
DWORD WINAPI ThreadSoc(LPVOID)
{
        while(true)
        {
                //индикатор разрешения приёма трансляции
                if (EnableReceive)
                {
                        Soc.Receive();  //вызов функции обработки принятых данных
                };
        };
}
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
        : TForm(Owner)
{
        //вызов функции создания сокета
        if (!Soc.Create()) return;
        else
        {

                //инициализация формата звукового файла
                CurrentFormatFile.wFormatTag=WAVE_FORMAT_PCM;
                CurrentFormatFile.nChannels=1;
                CurrentFormatFile.nSamplesPerSec=8000;
                CurrentFormatFile.nAvgBytesPerSec=8000;
                CurrentFormatFile.nBlockAlign=1;
                CurrentFormatFile.wBitsPerSample=8;
                CurrentFormatFile.cbSize=0;

                //инициализация устройств для записи и воспроизведения
                UINT uDeviceID=WAVE_MAPPER;

                //открытие устройства вывода звука для воспроизведения
                mres=waveOutOpen(&hwo,uDeviceID,&CurrentFormatFile,0,0,0);
                if (mres!=0) MessageBox(0,"Error: waveOutOpen",0,MB_OK);
                else btReceive->Enabled=true;  //активировать кнопку "Принимать"

                //открытие устройства ввода звука для записи
                mres=waveInOpen(&phwi,uDeviceID,&CurrentFormatFile,0,0,0);
                if (mres!=0) MessageBox(0,"Error: waveInOpen",0,MB_OK);
                else
                {
                        btSend->Enabled=true;  //активировать кнопку "Начать передачу"
                        From->Enabled=true;  //активировать поле "От кого"
                        To->Enabled=true;  //активировать поле "Кому"
                };
        };
}
//---------------------------------------------------------------------------
//кнопка Начать передачу
void __fastcall TForm1::btSendClick(TObject *Sender)
{
        Timer1->Enabled=true;  //разрешить срабатывание таймера на отправку пакетов

        btSend->Enabled=false;  //деактивировать кнопку "Начать передачу"
        btStopSend->Enabled=true;  //активировать кнопку "Остановить"
        From->Enabled=false;  //блокировка поля "От кого"
        To->Enabled=false;  //блокировка поля "Кому"
}
//---------------------------------------------------------------------------
//срабатывание таймера на отправку пакетов
void __fastcall TForm1::Timer1Timer(TObject *Sender)
{
        //подготовка буфера для записи
        pwhRec.lpData=WaveBuf;
        pwhRec.dwBufferLength=SIZE_OF_VOICE;
        pwhRec.dwFlags=0;

        //подготовка буфера для ввода аудиоданных
        mres=waveInPrepareHeader(phwi,&pwhRec, sizeof(pwhRec));
        if (mres!=0)
        {
                MessageBox(0,"Error: waveInPrepareHeader",0,MB_OK);
                return;
        };
        //отправка буфера заданному устройству записи
        mres=waveInAddBuffer(phwi,&pwhRec,sizeof(pwhRec));
        if (mres!=0)
        {
                MessageBox(0,"Error: waveInAddBuffer",0,MB_OK);
                return;
        };
        //ввод аудиоданных через заданное устройство ввода
        mres=waveInStart(phwi);
        if (mres!=0)
        {
                MessageBox(0,"Error: waveInStart",0,MB_OK);
                return;
        };

        //вызов функции отправки пакета
        Soc.Send(WaveBuf);
}
//---------------------------------------------------------------------------
//нажатие кнопки Остановить
void __fastcall TForm1::btStopSendClick(TObject *Sender)
{
        Timer1->Enabled=false;  //запретить срабатывание таймера на отправку пакетов

        btSend->Enabled=true;  //активировать кнопку "Начать передачу"
        btStopSend->Enabled=false;  //деактивировать кнопку "Остановить"
        From->Enabled=true;  //разблокировка поля "От кого"
        To->Enabled=true;  //разблокировка поля "Кому"
}
//---------------------------------------------------------------------------
//нажатие кнопки Принимать
void __fastcall TForm1::btReceiveClick(TObject *Sender)
{
        EnableReceive=true;  //индикатор разрешения проигрывания звука
        if (StreamOpen==false)  //индикатор создания потока
        {
                //создание потока
                Soc_id=CreateThread(0,0,ThreadSoc,0,0,&S_id);
                StreamOpen=true;  //индикатор создания потока
        };
        btReceive->Enabled=false;  //деактивировать кнопку "Принимать"
        btStopReceive->Enabled=true;  //активировать кнопку "Принимать"
}
//---------------------------------------------------------------------------
//срабатывание таймера на воспроизведение принятых данных
void __fastcall TForm1::Timer2Timer(TObject *Sender)
{
        //взять блок данных из начала очереди
        memcpy(PlayBuf,QUEUE[(fst+5)%SIZE_OF_BUF],SIZE_OF_VOICE);
        fst=(fst+1)%SIZE_OF_BUF;

        //подготовка буфера для воспроизведения
        pwhPlay.lpData=PlayBuf;
        pwhPlay.dwBufferLength=SIZE_OF_VOICE;
        pwhPlay.dwFlags=0;

PM MAIL   Вверх
Яхонт
Дата 30.4.2008, 11:37 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 6
Регистрация: 14.4.2008

Репутация: нет
Всего: 1



Я конечно не уверен, но у меня была аналогичная проблема, и ее причина крылась в следующем:



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

У каждого сэмпла есть время начала воспроизведения и время конца воспроизведения. Если сэмпл дошел слишком рано, то он придерживается и воспроизводится только когда настанет его время. Если сэмпл дошел слишком поздно, то он игнорируется или воспроизводится только его часть.

Теперь смотри, что на самом деле происходит. Это трудно описать текстом, но я все же попытаюсь. Аудиокарта генерирует сэмплы, времена которых (к примеру): 
1й сэмпл: от 0,5 сек до 0,51 сек
2й сэмпл: 0,51 сек - 0,52 сек
3й сэмпл: 0,52 сек - 0,53 сек
...... и т. д.

В этом случае процесс протекает так: 
В момент времени 0,495 сек из аудиокарты извлекается 1й сэмпл и передается на воспроизведение, где он придерживается до момента времени 0,5 сек, после чего выводится в динамик. Потом в момент времени где-то 0,505 сек из аудиокарты извлекается 2й сэмпл. Он также передается на воспроизведение, где придерживается до момента 0,51 сек, после чего воспроизводится в динамике, и т. д.

Предположим, что на отправку сэмпла по сети уходит 0,3 сек. Смотри, как меняется ситуация:
В момент времени 0,495 сек из аудиокарты извлекается 1й сэмпл и передается в сеть в течение 0,3 сек. В момент времени 0,795 он принимается на другой стороне и подается на воспроизведение, но он должен был проигрываться в момент 0,5 - 0,51 (отставание равно 0,295 сек). Тем временем на отправляющей стороне в момент времени 0,795 из аудиокарты извлекается 2й сэмпл (который должен был проигрываться с 0,51 сек по 0,52 сек) и передается по сети. 2й сэмпл принимаетс в момент времени 1,095 сек (отставание уже равно 0,585 сек), и т. д. Таким образом, звук, захваченный за 0,02 сек воспроизводится за 0,695 сек. Далее это отставание бесконечно возрастает и приводит к непредсказуемым результатам.


Чтобы выяснить, в этом ли дело, попробуй передавать звук не по сети, а в одном приложении простым копированием данных из буфера в буфер. Задержку сэмулируешь функцией Sleep(..). Тогда все выяснишь.



Удачи!


PM MAIL   Вверх
Бонифаций
Дата 30.4.2008, 15:07 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 827
Регистрация: 15.9.2005
Где: Brisbane

Репутация: нет
Всего: 40



может вал лучше что-нибудь типа ccrtp использовать? 
ccrtp


--------------------
 Бонифаций.
 
PM MAIL ICQ Skype GTalk Jabber YIM   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
1 Пользователей читают эту тему (1 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | C/C++: Сети | Следующая тема »


 




[ Время генерации скрипта: 0.0890 ]   [ Использовано запросов: 21 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.