Đăng nhập trang web tạo tên miền

Một phần của tài liệu Đồ án truyền âm thanh qua mạng 4g sd mã nguồn mở board nhúng raspberry (Trang 49)

Bước 2: Nhập thông tin tạo tài khoản

Email: phuongpham.god@gmail.com Password: xxxxxxxxxxxx

Hình 4.3: Nhập thơng tin để tạo tài khoản  Bước 3: Ta đăng kí một Hostname

Hostname: tcpstream / ddns.net Record Type: chọn DNS Host(A)

IP Address: địa chỉ IP của modem (tự động cập nhật theo modem của ta)

Hình 4.4: Tham số Host

Sau khi điền xong các tham số ta nhấn Add Host và kết quả là ta đã tạo được tên miền là tcpstream.ddns.net

Hình 4.5: Quản lí Host

4.1.3. Cài đặt modem định hướng gói tin

Để board có thể kết nối đến máy tính (PC) thơng qua mạng Internet thì ta phải có cơ sở hạ tầng mạng Internet thì thực hiện các cài đặt modem để có thể truy cập tới PC-Server ảo.

Muốn kết nối được thì bên phía client phải biết địa chỉ của server, nhưng ở đây ta chỉ biết được địa chỉ của modem còn địa chỉ của server là địa chỉ cục bộ,

không định tuyến được. Vậy ta phải cấu hình cho modem sao cho khi ta truyền dữ liệu tới modem thì modem sẽ gửi tới server bằng cách tạo ra server ảo.

Để cấu hình cho modem tạo server ảo (ở đây em dùng modem TP-Link) ta làm các bước sau:

Bước 1: Vào địa chỉ của modem thường mặc định là http://192.168.1.1 và đăng nhập tên người dùng và mất khẩu của modem (thông thường theo mặc định thì tên người dùng và mất khẩu là: admin)

Hình 4.6: Đăng nhập vào modem  Bước 2: Click vào Security Setup =>Port Forwarding Bước 2: Click vào Security Setup =>Port Forwarding

Bước 3: Mở Port

Application: Customer Settings Device: Manually Enter IP Address

IP Address: Địa chỉ IP LANcần mở port (192.168.1.153) TCP/UDP Port: Nhập port cần mở vào (9000)

Protocol: Chọn Both

Starting Port: Nhập port cần mở vào (9000) Ending Port: Nhập port cần mở vào (9000)

Bước 4: Sau khi click Add để cài thơng tin

Hình 4.9: Thơng tin Port đã mở

4.2. Thiết kế chương trình server trên máy tính4.2.1. Tng quan 4.2.1. Tng quan

Chương trình trên máy tính em dùng Visual Studio C# để thiết kế chương trình thu âm thanh (mở file audio) và truyền lên mạng Internet. Chương trình cũng gồm có 2 tiến trình chạy song song: 1 tiến trình dùng để thu âm thanh và 1 tiến trình dùng để truyền âm thanh đi.

Hình 4.10: Giao diện trên máy tính

4.2.2. Lp trình to Socket trên C#

Ta sẽ sử dụng các hàm mà C# đã hỗ trợ săn dùng cho việc tạo kết nối socket như:

IPAddress: Địa chỉ giao tiếp của một IP trên mạng.

Socket: là lớp cũng cấp nhiều phương thức và thuộc tính để giao tiếp mạng.

TcpListener: dùng để lắng nghe kết nối từ những máy trong mạng. Thiết lập miền địa chỉ IP và cổng để lắng nghe. Gọi phương thức Start() để có thể ắt đầu lắng nghe. AcceptSocket() là phương thức lắng nghe kết nối trả về. Khi đó đã kết nối được và có thể gửi và nhận dữ liệu.

Ví dụ: Đoạn chương trình mơ tả việc máy tính (có địa chỉ IP là x.x.x.x) nắng nghe kết nối tới máy khác (có cổng là Port_Number)

m_endpoint = new IPEndPoint(IPAddress.Parse(strIPAdress), Port); m_tcpip = new TcpListener(m_endpoint);

TcpClient client = m_tcpip.AcceptTcpClient();

4.2.3. Chương trình ghi d liu t mic

Lưu đồ thuật tốn chương trình ghi dữ liệu từ mic:

Ở đây ta sử dụng thư viện WinSound để thu âm thanh : private void StartRecordingFromSounddevice_Server() {

//Khởi tạo ghi âm

m_Recorder_Server = new WinSound.Recorder(); //Gọi sự kiện lấy dữ liệu và xử lí m_Recorder_Server.DataRecorded += new WinSound.Recorder.DelegateDataRecorded(OnDataReceivedFr omSoundcard_Server); m_Recorder_Server.Start(Config.SoundInputDeviceNameServe r, Config.SamplesPerSecondServer, Config.BitsPerSampleServer, Config.ChannelsServer, m_SoundBufferCount, bufferSize) m_JitterBufferServerRecording.Start(); }

Với xử lí âm thanh và đẩy vào buffer bằng hàmOnDataReceivedFromSoundcard_Server() :

private void OnDataReceivedFromSoundcard_Server(Byte[] data) { //Chia nhỏ dữ liệu int bytesPerInterval = WinSound.Utils.GetBytesPerInterval((uint) m_Config.SamplesPerSecondServer, m_Config.BitsPerSampleServer, m_Config.ChannelsServer); int count = data.Length / bytesPerInterval;

for (int i = 0; i < count; i++) {

Byte[] partBytes = new Byte[bytesPerInterval]; Array.Copy(data, currentPos, partBytes, 0, bytesPerInterval);

currentPos += bytesPerInterval;

WinSound.RTPPacket rtp = ToRTPPacket(partBytes, m_Config.BitsPerSampleServer,

m_Config.ChannelsServer);

// Đưa dữ liệu vào bộđệm JitterBuffer:

m_JitterBufferServerRecording.AddData(rtp); }

}

Khi tạo gói RTP, hầu hết các thơng tin như CSRC Count hoặc Version đều giống nhau. Sau mỗi gói RTP được gửi chỉ phải

tăng SequenceNumber và Timestamp. Trước đó, ta nén dữ liệu tuyến tính sang định dạng U-Law để giảm kích thước gói tin:

private WinSound.RTPPacket ToRTPPacket(Byte[] linearData, int bitsPerSample, int channels)

{

//Nén dữ liệu

Byte[] mulaws = WinSound.Utils.LinearToMulaw(linearData, bitsPerSample, channels);

//Tạo gói RTP

WinSound.RTPPacket rtp = new WinSound.RTPPacket(); rtp.Data = mulaws;

rtp.CSRCCount = m_CSRCCount; rtp.Extension = m_Extension; rtp.HeaderLength = WinSound.RTPPacket.MinHeaderLength; rtp.Marker = m_Marker; rtp.Padding = m_Padding; rtp.PayloadType = m_PayloadType; rtp.Version = m_Version; rtp.SourceId = m_SourceId;

//Tăng SequenceNumber và Timestamp sau mỗi gói tin try { rtp.SequenceNumber = Convert.ToUInt16(m_SequenceNumber); m_SequenceNumber++; } catch (Exception) { m_SequenceNumber = 0; } try { rtp.Timestamp = Convert.ToUInt32(m_TimeStamp); m_TimeStamp += mulaws.Length; } catch (Exception)

{

m_TimeStamp = 0; }

return rtp; }

Để gửi dữ liệu đi mà đảm bảo các gói tin gửi đi cách nhau một khoảng thời gian đều nhau, ta sử dụng thêm bộđệm JitterBuffer. Bộđệm JitterBuffer sẽđảm bảo các gói tin truyền đi với khoảng cách đều nhau, sau đây và tiến trình cài đặt bộđệm JitterBuffer và thêm sự kiện gửi gói tin:

m_JitterBufferServerRecording = new WinSound.JitterBuffer(null, RecordingJitterBufferCount, 20);

m_JitterBufferServerRecording.DataAvailable += new

WinSound.JitterBuffer.DelegateDataAvailable(OnJitterBufferServerD ataAvailable);

4.2.4. Chương trình phát âm thanh từ file audio

Lưu đồ thuật toán phát âm thanh từ file audio:

Để phát âm thanh từ file audio có sẵn, ta cần phải mở file audio :

private void ButtonOpenFileDialog_Click_1(object sender, EventArgs e) { if (OpenFileDialogMain.ShowDialog(this) == System.Windows.Forms.DialogResult.OK) { TextBoxFileName.Text = OpenFileDialogMain.FileName; } }

Đọc thông tin wav file từhàm WavFile trong thư viện WinSound : WinSound.WaveFileHeader header =

WinSound.WaveFile.Read(Config.FileName); Sau đó chia nhỏ dữ liệu :

m_RTPPartsLength =

WinSound.Utils.GetBytesPerInterval(header.SamplesPerSecond, header.BitsPerSample, header.Channels);

Chuyển đổi dữ liệu trước khi gửi :

m_PartByte = new Byte[m_RTPPartsLength];

Array.Copy(m_FilePayloadBuffer, m_CurrentRTPBufferPos, m_PartByte, 0, m_RTPPartsLength);

m_CurrentRTPBufferPos += m_RTPPartsLength; WinSound.RTPPacket rtp = ToRTPPacket(m_PartByte, m_FileHeader.BitsPerSample, m_FileHeader.Channels); Đưa dữ liệu vào bộ đệm JitterBuffer:

4.2.5. Gi d liu ti clients

Khi đã cài đặt bộ đệm JitterBuffer, JitterBuffer sẽ gọi tới hàm gửi sau mỗi 20ms:

private void OnJitterBufferServerDataAvailable(Object sender, WinSound.RTPPacket rtp)

{

Byte[] rtpBytes = rtp.ToBytes(); List<NF.ServerThread> list = new

List<NF.ServerThread>(m_Server.Clients); foreach (NF.ServerThread client in list) {

client.Send(rtpBytes); }

}

4.3. Thiết kế chương trình client trên board nhúng Raspberry Pi. 4.3.1. Tng quan 4.3.1. Tng quan

Để kết nối tới server để trao đổi dữ liệu ta dùng 1 socket để kết nối.

Client cần biết về sự tồn tại và địa chỉ của tiến trình server, nhưng server khơng cần biết về sự tồn tại và địa chỉ của client cho đến khi kết nối được thiết lập.

Mỗi khi kết nối được thiết lập, cả 2 bên đều có thể trao đổi (gửi và nhận dữ liệu).

Để thiết lập một kết nối cho cả 2 phía, cần xây dựng một socket. Có thể hiểu socket như một điểm đầu cuối của kênh kết nối giữa 2 tiến trình. Các hệ thống đều cung cấp các hàm hệ thống để thực hiện thiết lập một socket.

Trên board nhúng Raspberry, ta sử dụng ngơn ngữ lập trình C có thể biên dịch và chạy trên nền tảng HĐH Raspbian. Chương trình trên client có chức năng nhận dữ liệu từ mạng sau đó xử lý ra âm thanh và phát ra loa.

Giải thuật cho chương trình client:

 Nhận dữ liệu theo gói RTP từ server thông qua socket  Giải mã âm thanh

 Phát âm thanh.

4.3.2. Lp trình socket trên client

Mơ hình bài tốn:

Hình 4.13: Mơ hình lập trình socket giữa client và server Các bước thiết lập một socket phía client gồm: Các bước thiết lập một socket phía client gồm:

 Tạo một socket bằng hàm socket()

 Kết nối socket đến địa chỉ của server bằng hàm connect()

 Gửi và nhận dữ liệu: Có một số cách khác nhau, đơn giản nhất là sử dụng các hàm read() và write()

Các bước thiết lập một socket phía server gồm:  Tạo một socket bằng hàm socket()

 Gắn (bind) socket đến địa chỉ của server sử dụng hàm bind().

Đối với server trên internet địa chỉ bao gồm địa chỉ ip của máy host + số hiệu cổng dịch vụ (port number)

 Lắng nghe (listen) các kết nối đến từ clients sử dụng hàm listen()  Chấp nhận các kết nối sử dụng hàm accept(). Hàm này sẽ dừng

(block) cho đến khi nhận được một client kết nối đến.  Gửi và nhận dữ liệu với client (hàm read(), write())  Đóng kếtnối bằng hàm close()

Chương trình khởi tạo socket trên client: void init_socket()

{

int portno=9000;

struct sockaddr_in serv_addr;

sockfd = socket(AF_INET, SOCK_STREAM, 0); if (sockfd < 0)

printf("ERROR opening socket");

memset(&serv_addr, '0', sizeof(serv_addr)); serv_addr.sin_family = AF_INET; serv_addr.sin_port = htons(portno); if(inet_pton(AF_INET,"118.71.4.45", &serv_addr.sin_addr)<=0) {

printf("\n inet_pton error occured\n"); }

if( connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0)

{

printf("\n Error : Connect Failed \n"); }

}

4.3.3. Lập trình đa luồng

Chương trình lập trình cho board được chia làm 2 tiên trình chạy song song với nhau. 2 tiến trình gồm: 1 tiến trình gửi yêu cầu và nhận dữ liệu từ server và 1 tiến trình phát âm thanh nhận được ra đầu ra audio (loa).

Để tạo được 2 tiến trình chạy song song ta dùng giải pháp lập trình đa luồng (thread). Mỗi thread xác định một luồng đơn, thực thi trong phạm vi một chương trình. Khi một chương trình thực thi trên CPU, nó thực hiện các câu lệnh chương trình trong một luồng đơn tới khi luồng hoàn thành.

Viết ứng dụng đơn giản thực hiện tạo luồng, ta dùng hàm pthread_create. pthread_create(&thread_id1, NULL, &nhan_du_lieu, NULL);

pthread_create(&thread_id2, NULL, &phat_am, NULL); Ví dụ: tạo 2 luồng in ra màn hình 2 chữ x và o đan xen nhau.

#include <stdio.h> #include <pthread.h>

void* print_x(void* unused) { while(1) { fputc('x',stdout); }return NULL; }

{

pthread_t thread_id;

pthread_create(&thread_id, NULL, &print_x, NULL); while(1) { fputc('o',stdout); } return 0; }

4.3.4.Thư viện TinyALSA

TinyALSA là một thư viện nhỏ để giao tiếp với ALSA trong nhân Linux, nó cung cấp API pcm và mixer cơ bản.

4.3.4.1. Cài thư viện TinyALSA trên Raspberry

Copy thư viện tinyalsa đã giải nén vào raspberry sau đó và cửa sổ lệnh gõ các lệnh sau (Vào thư trong mục tinyalsa bằng lệnh cd “tên thư mục “ ví dụ cd tinyalsa):

make

sudo make install sudo ldconfig

Sau khi hoàn tất 3 lệnh trên thư viện tinyalsa sẽ cài xong. Để biên dịch chương trình.ta dùng lệnh trên Terminal

gcc -o source source.c -ltinyalsa –lpthread (Trong đó source.c là thư mục chứa code, biên dịch bằng gcc được tệp thực thi source , khai báo 2 thư viện sử dụng là ltinyalsa và lpthread).

4.3.4.2. Hàm x lí âm thanh trên thư viện TinyALSA

Thư viện TinyALSA có các hàm hỗ trợ xử lý âm thanh sau:

-card: Xác định card cho pcm. Card mặc định bằng 0. -device: Xác định device sử dụng, device mặc định bằng 0

-flags: Chỉđịnh đặc điểm và chức năng về pcm. Đặt flags = PCM_OUT để phát -config: Các thông số phần cứng và phần mềm để mở PCM.Được khai báo dạng const struct pcm_config. Trong đó có cài đặt tốc độđọc, số kênh, định dạng pcm, Độ lớn của chu kỳ hoạt động pcm

*Giá trị trả về dạng PCM struct

* Nếu xảy ra lỗi ở cấp phát bộ nhớ cho PCM, trả về NULL.

* Mặt khác, client phải kiểm tra xem PCM đã mở đúng bằng cách gọi hàm kiểm tra pcm_is_ ready()

pcm_writei(struct pcm *pcm, const void *data, unsigned int frame_count)

Ghi audio samples vào PCM.

* Nếu PCM chưa được khởi động, nó được khởi động trong hàm này. * Chức năng này chỉ hợp lệ cho các PCM được mở bằng cờ PCM_OUT. - pcm: PCM được trả về bởi hàm pcm_open()

- data: Mảng audio samples

- frame_count: Sốlượng frameschứa mảng samples.

* return: Khi hoàn thành, chức năng này trả về sốlượng frames được viết; nếu không trả về một số âm.

pcm_close(struct pcm *pcm)

Hàm này có chức năng đóng PCM struct được tạo bởi hàm pcm_open() - pcm : PCM được trả về bởi pcm_open()

4.3.5. Nhn và x lý âm thanh

Chương trình lập trình cho board được chia làm 2 tiến trình chạy song song với nhau. 2 tiến trình gồm: 1 tiến trình gửi yêu cầu và nhận dữ liệu từ server và 1 tiến trình phát âm thanh nhận được ra đầu ra audio (loa).

Để tạo được 2 tiến trình chạy song song ta dùng giải pháp lập trình đa luồng (thread). Mỗi thread xác định một luồng đơn, thực thi trong phạm vi một chương trình. Khi một chương trình thực thi trên CPU, hai luồng chạy song song với nhau.

Hình 4.15: Buffer[] để lưu và đọc dữ liệu.  Các hàm sử dụng trong tiến trình nhận dữ liệu.  Các hàm sử dụng trong tiến trình nhận dữ liệu.

void rev() {};

Hàm rev() sử dụng hàm read () để đọc data từ server và lưu vào revbuff.

void* nhan_du_lieu(void* unused){ };

Hàm này đọc data từ revbuff và lưu vào buffer[][].Kiểm tra bộ đệm còn đọc tiếp bằng hàm incpin().

charincpin(){ } ;

Hàm icnpin() tăng pin thêm 1,trả về 0 nếu pin = pout hoặckhi đầy bộđệm.

 Các hàm sử dụng trong tiến trình phát âm thanh

Trước khi sử dụng phát âm thanh, Vì dữ liệu nhận được đã được nén lại dạng MuLaw nên ta giải nén âm thanh về Linear.

unsigned int MulawToLinear(unsigned int ulaw) {

ulaw = ~ulaw;

int t = ((ulaw & QUANT_MASK) << 3) + BIAS; t <<= (ulaw & SEG_MASK) >> SEG_SHIFT;

return ((ulaw & SIGN_BIT) > 0 ? (BIAS - t) : (t - BIAS)); }

void MuLawToLinear(unsigned char * bytes, int bitsPerSample, int channels)

{

int blockAlign = channels * bitsPerSample / 8; unsigned char *result;

result = outbuff;

for (int i = 0, counter = 0; i < N; i++, counter += blockAlign) {

unsigned int value = MulawToLinear(bytes[i]); unsigned char values0 =value % 256;

unsigned char values1 =value / 256; result[counter] = values0;

result[counter + 1] = values1; }

}

Sử dụng hàm write_frame() ghi vào bộđệm để phát âm thanh. static int write_frames()

{

unsigned int device = 0; int flags = PCM_OUT; int j,err;

const struct pcm_config config = { .channels = 1, .rate = 8000, .format = PCM_FORMAT_S16_LE, .period_size = 1024, .period_count = 2, .start_threshold = 1024, .silence_threshold = 1024 * 2, .stop_threshold = 1024 * 2 };

struct pcm * pcm = pcm_open(card, device, flags, &config); if (pcm == NULL) {

fprintf(stderr, "failed to allocate memory for PCM\n"); return -1; }

else if (!pcm_is_ready(pcm)){ pcm_close(pcm);

fprintf(stderr, "failed to open PCM\n"); return -1; } while(1) { if (pout != pin) { MuLawToLinear(&buffer[pout][j],16,1); pout = pout +1; if (pout>=M) pout=0;

}

pcm_close(pcm); return 0;

}

static int write_frames(){};

Hàm này khởi tạo pcmbằng pcm_open()

Chuyển dữ liệu dạng MuLaw về Linear bằng hàm MuLawToLinear() Lưu kết quả vào bộđệm outbuff [].

Các hàm xử lý pcm được hỗ trợ trên thư viện TinyALSA Sau đó ta thực hiện phát âm thanh:

void* phat_am(void* unused) { if (write_frames() < 0) { return(NULL); } return(NULL); }

4.4. Kết qu thc tế.

Sau khi hoàn thành hệ thống, em đã thu được kết quả như sau:

Hình 4.17: Hình ảnh bên phía server

Hình 4.18: Hình ảnh bên phía client

Kết luận chương

Qua chương 4 chúng ta đã xây dựng và viết phần mềm chương trình truyền âm thanh qua mạng và thử nhiệm với mơ hình thực tế.

KT LUN CHUNG

Sau một thời gian thực hiện đến nay đồ án của chúng em đã hoàn thành đúng thời gian quy định với yêu cầu đề tài đặt ra là “Nghiên cứu mã nguồn mở, board nhúng Raspberry Pi cho hệ thống phát âm thanh qua mạng 4G”.

Qua quá trình làm đồ án chúng em đã học được cách tra cứu tài liệu. Tìm hiểu về các đặc điểm của board nhúng Raspberry Pi, kiến thức về mạng Internet. Tìm hiểu được cấu trúc và lập trình truyền thơng qua mạng Internet.

Hướng phát triển của đề tài:

Ngày nay các hệ thống loa phường, loa thông báo bằng dây đang dần thể hiện sự bất cập vì việc đi dây phức tạp, truyền âm thanh qua mạng 4G đang có lợi thế lớn để thay thế cho các hệ thống cũ này.

Trong quá trình thực hiện đồ án, chúng em nhận được sự chỉ bảo tận tình của các thầy, cô giáo trong khoa và đặc biệt là thầy giáo Thiếu tá, Thc s Nguyễn Văn Xuân đã giúp em hồn thành tốt đồ án tốt nghiệp của mình.

TÀI LIU THAM KHO TING VIT:

[1] Nguyễn Ngọc Bình Phương – Thái Thanh Phong, Các gii pháp lp trình C#

TING ANH:

[2] Michael Barr, O’Reilly andAssociates, Programming Embedded Systems in

C and C++, Website tham kho: https://github.com/tinyalsa/tinyalsa https://sites.google.com/site/embedded247/npcourse/lap-trinh-c-socket https://voer.edu.vn/m/bo-giao-thuc-rtprtcp/3d1c93bb https://tools.ietf.org/html/rfc3550 http://www.faqs.org/rfcs/rfc793.html https://www.ibm.com/support/knowledgecenter/en/SSLTBW_2.3.0/com.ibm.zos.v 2r3.bpxbd00/socket.htm

PHC LC 1. Code chương trình client

#include <stdio.h> #include <stdlib.h> #include <tinyalsa/pcm.h> #include <pthread.h> #include <unistd.h> #include <string.h> #include <sys/types.h> #include <sys/socket.h> #include <netinet/in.h> #include <netdb.h> #include <arpa/inet.h>

const int N = 160; // do dai du lieu truyen

const int M = 1000; // so khung du lieu cua bo dem unsigned char buffer[1000][400];

Một phần của tài liệu Đồ án truyền âm thanh qua mạng 4g sd mã nguồn mở board nhúng raspberry (Trang 49)

Tải bản đầy đủ (PDF)

(83 trang)