LỜI NÓI ĐẦU
Trong những năm gần đây, mạng máy tính ngày càng trở nên phổ biến. Việc liên kết các máy tính trên môi trường mạng cũng như liên kết các mạng lại với nhau đem lại cho chúng ta nhiều lợi ích trong công việc cũng như trong việc học tập nghiên cứu, giải trí. Chúng ta có thể sử dụng các tài nguyên sẵn có được chia xẻ như file server, printer, máy fax, . môi trường mạng còn là một môi trường thông tin nhanh chóng và tiện lợi nhờ vào các cơ chế truyền thông trên mạng như : e-mail, www .
Bên cạnh đó, tốc độ phát triển của máy tính PC cũng rất nhanh chóng. Các kỹ thuật hiện đại đã giúp tạo ra các máy PC với tốc độ tính toán nhanh hơn, bộ nhớ lớn hơn và khả năng xử lý của nó cũng ngày càng đa dạng hơn trong khi giá thành ngày càng rẻ hơn. Một trong những khả năng ưu việt của máy PC hiện nay là hỗ trợ multimedia. Các máy PC ngày nay giao tiếp với con người không chỉ bằng text mà còn kết hợp tất cả các phương tiện khác như tiếng nói, hình ảnh.
Việc đưa kỹ thuật multimedia vào các ứng dụng truyền thông trên mạng giúp chúng ta tạo ra nhiều ứng dụng phong phú hơn. Chẳng hạn hộp thư điện tử ngày nay có thể không chỉ là văn bản mà còn bao gồm tiếng nói, hình ảnh. Các trang web trở nên sinh động hơn hẳn khi kèm theo kỹ thuật multimedia. Bên cạnh đó, chúng ta có thể thiết kế các ứng dụng tiện ích như Video conference, voice mail .
Thông qua chương trình này, người sử dụng có thể trao đổi thông tin với nhau bằng tiếng nói. Chương trình này đã được hiện thực rất nhiều trong các lĩnh vực thông tin như điện thoại, viễn thông, máy tính . Tuy nhiên nó chưa được áp dụng và phát triển rọng rãi như trên các lĩnh vực thông tin khác do sự hạn chế của thiết bị. Ngày nay, khi công nghệ thông tin đã phát triển thì việc hiện thực chương trình này là hoàn toàn có thể. Ứng dụng trong nhiều lĩnh vực khác nhau như :
ü ãViệc dạy học từ xa.
ü ãViệc chẩn đoán, chữa bệnh từ xa.
ü ãHội thảo, thảo luận theo nhóm.
ü ãCông cụ trao đổi thông tin bằng hình ảnh và âm thanh.
Mục tiêu của đồ án tốt nghiệp là tìm hiểu các mô hình và công nghệ truyền âm thanh trên mạng máy tính, trên cơ sở đó xây dựng ứng dụng truyền thông âm thanh thoại trên mạng cục bộ. Đồ án sẽ xây dựng thử nghiệm một hệ thống cho phép trao đổi thông tin bằng tiếng nói thoại, tương tác điểm – điểm trên mạng LAN. Đồ án được trình bày gồm 5 chương với bố cục như sau :
Chương I : Tìm hiểu các mô hình điện thoại qua mạng, Từ đó đưa ra mô hình sẽ thực thi trong đồ án này.
Chương II: Giới thiệu chung về các giao thức truyền thông trên mạng Internet và vào khảo sát cụ thể các giao thức này.
Chương III : Giới thiệu các chuẩn mã hoá và nén âm thanh.
Chương IV : Tìm hiều môi trường lập trình SDK Windows và ứng dụng trên mạng.
Chương V : Thiết kế chương trình truyền tiếng nói qua mạng LAN thông qua sự trợ giúp của công cụ SDK. Đánh giá và các kết quả thử nghiệm.
Phần kết luận : Nêu những kết quả của đề tài và hướng nghiên cứu hướng phát triển tiếp theo.
Việc nghiên cứu lý thuyết một cách hệ thống và xây dựng chương trình phần mềm đòi hỏi phải đầu tư nhiều thời gian. Với thời gian có hạn cho nên bài luận văn này của em không tránh khỏi những thiếu sót, em rất mong được sự chỉ dẫn thêm của thầy cô và các bạn.
Nhân đây, em xin chân thành cám ơn đến PGS - TS Nguyễn Thị Hoàng Lan, người trực tiếp hướng dẫn em hoàn thành cuốn luận văn này. Em xin chân thành cám ơn các thầy cô trong khoa Công nghệ thông tin Trường ĐH Bách Khoa HN, Trường ĐH Thủy Sản và toàn thể các bạn đã giúp đỡ em hoàn thành cuốn luận văn này.
64 trang |
Chia sẻ: lvcdongnoi | Lượt xem: 2439 | Lượt tải: 0
Bạn đang xem trước 20 trang tài liệu Luận văn Xây dựng thử nghiệm một hệ thống cho phép trao đổi thông tin bằng tiếng nói thoại, tương tác điểm – Điểm trên mạng LAN, để xem tài liệu hoàn chỉnh bạn click vào nút DOWNLOAD ở trên
cấu trúc WAVEHDR để miêu tả khối dữ liệu trên.
Trước khi sử dụng các hàm trên để gửi khối dữ liệu cho thiết bị. Chúng ta phải cấp phát vùng nhớ cho khối dự liệu và khối header. Khối header phải được prepare và unprepare bằng các hàm sau:
Hàm
Chức năng
WaveInPrepareHeader
Prepare khối dữ liệu input
WaveInUnprepareHeader
Unprepare khối dữ liệu input
WaveOutPrepareHeader
Prepare khối dữ liệu output
WaveOutUnprepareHeader
Unprepare khối dữ liệu output
Trước khi gửi khối dữ liệu cho driver input hay output, chúng ta phải prepare chúng. Sau khi thiết bị sử dụng xong, các khối dữ liệu phải được unprepare trước khi giải phóng các vùng nhớ đã cung cấp.
Khi kích thước dữ liệu lớn, chúng ta phải cung cấp các buffer liên tục cho thiết bị, quá trình này phải diễn ra liên tục cho đến khi hoàn tất công việc và thiết bị được đóng lại.
Ứng dụng phải xác định và quản lý thời điểm mà thiết bị hoàn tất việc thự thi trên các khối dữ liệu để đưa ra các tác động thích hợp. Các cách sau đây được đưa ra:
Chi định hàm callback nhận Thông báo mà thiết bị gửi khi nó hoàn tất một khối dữ liệu.
Sử dụng các event callback
Chỉ định window hay thread nhận Thông báo gửi từ thiết bị.
Xác định bit WHDR_DONE trong cờ dwFlags của cấu trúc WAVEHDR đi kèm với mỗi khối dữ liệu.
Khi ứng dụng không đáp ứng được tốc độ xử lý các buffer thì chiến lược buffer kép có thể được đưa ra để tăng tốc độ thực thi.
Chúng ta sẽ khảo sát một số phương thức xử lý sau khi thiết bị hoàn tất một khối dữ liệu.
Dùng hàm callback để xử lý các driver messages
Để chỉ định hàm callback xử lý ứng vớicác driver message, chúng tachỉ định cờ CALLBACK_FUNCTION trong biến fdwOpen và địa chỉ hàm xử lý trong biến dwCallback khi gọi hàm waveInOpen hay waveOutOpen.
Messages gửi cho hàm callback tương tự như Thông báo gửi cho window, ngoại trừ việc nó có hai thông số DWORD thay vì một thông số DWORD và một thông số UINT.
Để gửi dữ liệu cho hàm callback chúng ta có thể dùng một trong hai cách sau:
Dùng thông số dwInstance trong hàm open device
Dùng field dwUser trong cấu trúc WAVEHDR để chỉ định khối dữ liệu gửi cho device driver.
Dùng event callback xử lý các driver message
Để dung event callback, chúng ta dùng hàm CreateEventđể truy xuất handle của event. Trong hàm open thiết bị, chỉ định cờ CALLBACK_EVENT cho thông số fdwOpen. Sau khi gọi hàm waveOutPrepareHeader nhưng trước khi gửi dữ liệu cho thiết bị, chúng ta tạo ra một nonsignal event bằng cách gọi hàm ResetEvent, chỉ định event handle được lấy từ hàm CreateEvent. Trong vòng loop để kiểm tra khi bit WHDR_DONE được set trong cấu trúc WAVEHDR, chúng ta gọi hàm WaitForSingleObject, chỉ định thông số event handle và giá trị time-out là INFINITE. Giá trị event callback là giá trị dùng gọi hàm callback.
Bởi vì event callback không xác định được thông báo xác định close, done hay open. Ứng dụng phải kiểm tra tình trạng của hệ thống đang chờ sự kiện gì xảy ra để đưa ra các đáp ứng chính xác.
Dùng window hay thread để xử lý các message driver
Để dùng hàm window callback, chúng ta chỉ định thông số CALLBACK_ WINDOW trong biến fdwOpen và chỉ định handle của window trong thông số dwCallback khi gọi hàm open thiết bị. Driver message sẽ được gửi tới window procedure.
Tương tự như vậy, chúngta sẽchỉ định thông số CALLBACK_THREAD và thread handle trong hàm open khi chúng ta muốn thread xử lý các driver message.
Ngoài cách thức dùng hàm callback, chúng ta có thể dựa vào thông số dwFlags trong WAVEHDR để xác định xem thiết bị có hoàn tất việc xử lý khối dữ liệu hay chưa.
Các hàm kiểm tra lỗi
Các hàm waveform audio sẽ trả về giá trị khác 0 khi có lỗi xảy ra. Windows cung cấp cho chúng ta các hàm xác định lỗi dựa trên các thông số này. Ứng dụng sẽ dựa vào các thông số xác định lỗi để quyết định công việc thực thi tiếp tục. Các hàm sau được dùng để xác định các lỗi xảy ra:
Hàm
Chức năng
WaveInGetErrorText
Trả về chuỗi text xác định lỗi xảy ra của input device
WaveOutGetErrorText
Trả về chuỗi text xác định lỗi xảy ra của output device
IV.2 KỸ THUẬT TRUYỀN NHẬN ÂM THANH TRÊN MẠNG IP
IV.2.1 MÔ HÌNH LIÊN KẾT VÀ TRAO ĐỔI DỮ LIỆU
Chương trình dùng giao thức TCP/IP làm giao thức giao tiếp. Việc thiết lập liên kết cũng như trao đổi dữ liệu đều tuân theo các cấp của giao thức này. Việc gọi và thiết lập liên kết được thực hiện theo mô hình client/server, việc trao đổi dữ liệu được thực hiện thông qua socket theo giao thức TCP.
Có hai ý tưởng được đưa ra trong việc dùng socket để trao đổi dữ liệu.
Dùng 1 socket :
Mỗi máy dùng một socket để truyền nhận dữ liệu. Theo giao thức TCP sau khi hai socket connect được với nhau thì việc tiến hành trao đổi dữ liệu sẽ bắt đầu. Chúng ta sẽ dùng cặp socket này. Như vậy, một socket trên một máy đồng thời đảm nhận việc truyền dữ liệu đi cũng như nhận dữ liệu về.[3]
socket
socket
Yêu cầu truyền dữ liệu
Yêu cầu nhận dữ liệu
socket
Hình IV.1 Mô hình dùng 1 socket
Cách dùng này có đặc điểm là việc tạo liên kết đơn giản, quá trình tạo liên kết hoàn toàn giống như các bước trong việc tạo liên kết giữa các socket dùng giao thức TCP. Chương trình chạy và lắng nghe ở một port xác định. Khi có một yêu cầu gọi liên kết đến, chương trình sẽ tạo ra một socket để nối kết với socket gọi. Sau khi thiết lập liên kết thì các socket bắt đầu gửi nhận dữ liệu. Socket sẽ gửi dữ liệu âm thanh đi đồng thời nhận dữ liệu truyền tới và chuyển cho hệ thống xử lý.
Socket làm việc theo cách này sẽ nhận hai thông báo cùng một lúc. Khi có dữ liệu từ mạng truyền tới, hệ thống sẽ thông báo cho socket để tiến hành việc nhận dữ liệu. Cũng tương tự như vậy, khi có dữ liệu âm thanh sẵn sàng, hệ thống cũng sẽ gọi socket để truyền đi.
Như vậy, khi thực thi socket sẽ nhận được hai thông báo của hệ thống. Vì việc truyền nhận dữ liệu âm thanh là dạng dữ liệu liên tục cho nên tần suất mà hệ thống thông báo cho socket là rất thường xuyên. Vì vậy, socket trong cùng một lúc có thể nhận được cả hai yêu cầu truyền dữ liệu đi và nhận dữ liệu về. Thêm vào đó các hoạt động truyền nhận dữ liệu là các hoạt động bị tắc nghẽn. Do đó chúng ta phải lưu ý đến hiện tượng này, socket có thể đáp ứng không kịp nhu cầu của hệ thống.
Chúng ta lấy một trường hợp ví dụ. Khi socket nhận được yêu cầu truyền dữ liệu đi, nó sẽ lấy dữ liệu từ các buffer và truyền đi. Do quá trình truyền dữ liệu có thể bị tắc nghẽn, socket sẽ phải chờ. Đồng thời trong lúc này, nó lại nhận được tín hiệu thông báo có buffer kế tiếp cần truyền đi và tín hiệu thông báo có dữ liệu trên mạng truyền về. Với các yêu cầu dồn dập như vậy, hệ thống có thể sẽ đáp ứng không kịp và chương trình có thể bị treo.
Vì vậy, khi dùng một socket để truyền nhận dữ liệu, chúng ta phải tính toán cân đối thời gian giữa việc truyền dữ liệu đi và việc nhận dữ liệu về sao cho hợp lý để hệ thống có thể làm việc liên tục được. Chúng ta có thể qui định thời gian cho việc truyền nhận. Trong một thời điểm socket có thể chỉ làm việc truyền dữ liệu đi, các yêu cầu nhận dữ liệu sẽ bị ngưng lại. Sau đó socket sẽ chỉ xử lý các yêu cầu nhận dữ liệu. Chiến lược này giúp giảm nhẹ hoạt động của socket. Tuy nhiên, chúng ta cần áp dụng cho cả hai socket liên kết. Trong một thời điểm, một socket sẽ truyền còn socket còn lại sẽ nhận dữ liệu, và thời điểm sau thì quá trình sẽ diễn ra theo chiều ngược lại.
Dùng 2 socket :
Xuất phát từ ý tưởng trên, chúng ta có thể dùng hai socket trong việc trao đổi dữ liệu. Một liên kết hình thành giữa hai máy sẽ gồm hai cặp socket liên kết với nhau. Một socket chỉ đảm nhận việc truyền dữ liệu trong khi socket còn lại đảm nhận việc nhận dữ liệu.[3]
Socket truyền
Socket truyền
Socket nhận
Socket nhận
Yêu cầu truyền
dữ liệu
Yêu cầu nhận
dữ liệu
Socket truyền
Socket nhận
Hình IV.2 Mô hình dùng 2 socket
Vì mỗi socket chỉ nhận một tín hiệu nhất định. Socket truyền sẽ chỉ chú ý tới tín hiệu báo có dữ liệu của hệ thống để tiến hành truyền dữ liệu đi. Trong khi đó, socket nhận sẽ chỉ lưu ý đến tín hiệu báo có dữ liệu của hệ thống. Hai socket sẽ hoạt động độc lập với nhau và công việc của một socket sẽ nhẹ nhàng hơn mô hình trên.
Tuy nhiên, trong mô hình này, việc thiết lập liên kết giữa hai máy sẽ trở nên phức tạp hơn. Theo mô hình client/server, khi một socket gọi và thiết lập liên kết với chương trình ở máy remote xong thì máy remote cũng phải tạo ra một socket và tiến hành liên kết ngược lại. Sau khi cặp socket hoàn toàn liên kết xong thì hai máy mới coi như đã connect và tiến hành truyền nhận dữ liệu.
Một khía cạnh khác cần lưu ý là tuy hai socket hoạt động độc lập với nhau nhưng chúng đều thuộc cùng một chương trình và chúng đều tiến hành việc gửi nhận dựa trên các giao thức lớp dưới chung. Do đó, trong một thời điểm chỉ có một hoạt động diễn ra hoặc là truyền dữ liệu hoặc là nhận dữ liệu. Vì vậy thật ra hai socket cũng phải hoạt động phụ thuộc nhau. Socket gửi dữ liệu phải chờ socket nhận nhận xong dữ liệu rồi mới bắt đầu truyền đi và ngược lại việc truyền dữ liệu phải được hoàn tất thì việc nhận dữ liệu mới có thể tiến hành được.
Một vấn đề khác nẩy sinh do đặc điểm của dữ liệu. Dữ liệu tiếp nhận là dạng dữ liệu liên tục do đó, các tín hiệu mà hệ thống báo cho hai socket cũng xảy ra liên tục, vì vậy thực sự rằng tuy chỉ làm một công việc nhưng khối lượng công việc mà socket phải đảm nhận là rất lớn. Thêm vào đó, hai socket đều phụ thuộc vào một process do đó thật sự xét về mặt thực thi của quá trình thì khả năng giảm nhẹ công việc là không bao nhiêu. Và khả năng hệ thống bị treo do quá tải cũng vẫn có thể xảy ra.
Chúng ta có những cách giải quyết để giảm nhẹ việc thực thi của chương trình như dùng cơ chế xử lý song song (thread) hay dùng cơ chế phân chia thời gian cho các hoạt động như đã nói ở trên.
IV.2.2 CƠ CHẾ GỌI VÀ XÁC LẬP LIÊN KẾT
Khi liên kết được xác lập, chúng ta sẽ bắt đầu tiến hành trao đổi dữ liệu. Tuy nhiên trước hết chúng ta cần khảo sát phương pháp gọi cũng như thiết lập liên kết. Chương trình được hiện thực dựa trên cơ chế client/server cho nên việc tạo liên kết cũng dựa trên cơ chế này. Ý tưởng chính là: khi chương trình bắt đầu thực thi, nó cũng bắt đầu lắng nghe lời gọi liên kết ở một port xác định. Thực sự, trong chương trình chúng ta sẽ tạo ra một socket server và lắng nghe ở một port qui ước trước. Khi một socket khác muốn tạo liên kết, nó sẽ tiến hành gọi liên kết với socket server ở giá trị port này.[3]
Trong giao thức TCP/IP, một quá trình giao tiếp thông qua môi trường mạng phải có một chỉ số port xác định. Các quá trình khác nhau phải có port khác nhau. Khi thiết kế mô hình client/server, các nhà thiết kế đã tạo ra một số dịch vụ thông dụng trên mạng như: finger, echo, mail, ftp . . . Các server của các dịch vụ này được dành sẵn các port xác định mà không một quá trình nào được phép sử dụng. Các port này được gọi là well-known port và do hệ thống cấp phát và quản lý. Thông thường, các chỉ số well-known port có giá trị từ 0 đến 1023. Các ứng dụng không được phép sử dụng giá trị port trong khoảng này. Ứng dụng có thể dùng các giá trị port từ 1024 trở đi. Ví dụ: khi chúng ta cần tạo một socket mà không cần quan tâm đến giá trị port, chúng ta có thể nhờ hệ thống cấp cho một giá trị port còn trống. Thông thường các giá trị port mà hệ thông cung cấp cho ứng dụng khi có yêu cầu nằm trong khoảng từ 1024 đến 5000. Còn khi chúng ta muốn chỉ định một giá trị port cho socket, chúng ta sẽ có thể chọn giá trị từ 5000 trở đi. Vì trong vùng này xác suất mà port đó đã bị chiếm là rất hiếm.
Vì vậy, khi thiết kế chúng ta muốn tạo một port cố định thì nên chọn socket lắng nghe ở một port có giá trị lớn hơn 5000. Giá trị được chọn là 7699 nhưng mô hình của chúng ta là : trong một chương trình vừa có đóng vai trò là client vừa là server nên ta chọn port có thể thay đổi được trong khoảng từ 1024 đến 5000.
Khi muốn tạo liên kết, chúng ta sẽ tạo một socket và tiến hành connect vào socket đang lắng nghe ở một địa chỉ và port lắng nghe. Khi socket listen nhận thấy có yêu cầu liên kết, nó sẽ thông báo cho người sử dụng biết. Nếu nguời sử dụng đồng ý thì nó sẽ tiến hành connect và việc trao đồi dữ liệu bắt đầu. Nếu người sử dụng từ chối thì ứng dụng sẽ thông báo cho phía gọi lời từ chối và đóng liên kết lại.
Chúng ta nói thêm về địa chỉ khi liên kết. Do chương trình hiện thực trên môi trường mạng Windows là môi trường mạng workgroup. Mỗi máy được xem như một host riêng lẻ. Nếu trên mạng không có các server như server novell hay server NT thì chúng ta không thể biết được các thông tin về một máy remote nếu chúng ta không tạo liên kết với máy đó. Vì vậy, trong cơ chế liên kết, chúng ta chọn việc định địa chỉ để liên kết. Một máy muốn thiết lập liên kết với một máy khác thì phải nhập thông số là địa chỉ IP của máy đó.
IV.2.3 CƠ CHẾ TRUYỀN NHẬN DỮ LIỆU
Khi viết một ứng dụng trên môi trường Windows, chúng ta phải lưu ý đến đặc điểm của môi trường Windows là môi trường có kiến trúc message-driven. Windows được xem là một môi trường có kiến trúc message-driven hay event-driven vì không một chương trình nào trên windows có thể thực thi nếu không có một Thông báo hay một sự kiện kích khởi nó. Trong môi trường Windows luôn tồn tại một vòng lặp message loop. Vòng message loop này sẽ truy xuất các Thông báo từ các hàng chờ của các chương trình và tùy theo loại Thông báo hay sự kiện, nó cho phép window procedure tương ứng thực thi. Vì vậy trên môi trường Windows có thể tồn tại nhiều ứng dụng cùng một lúc mỗi ứng dụng có một hàng chờ Thông báo riêng. Khi có một sự kiện xảy ra, hệ thống sẽ xác định xem sự kiện đó tuơng ứng với ứng dụng nào và chuyển Thông báo đến hàng chờ của ứng dụng tương ứng đó. Tùy theo loại Thông báo mà ứng dụng sẽ gọi chương trình tương ứng thực thi.
Môi trường windows 16 bits là môi trường nonpreemptive, có nghĩa là khi một ứng dụng đang xử lý một Thông báo thì không một ứng dụng nào có thể thực thi được. Phải chờ cho đến khi procedure của ứng dụng tiến hành xong công việc và trả về thì lúc đó procedure tương ứng với Thông báo tiếp theo trong hàng chờ mới được thực thi. Trong khi đó các môi trường Win32 như Windows98, WindowsNT lại thực thi theo cơ chế preemptive. Trong môi trường này, việc procedure nào được thực thi là do hệ thống quyết định. Thật ra, trong môi trường Win32, hệ thống định thời cho các thread thực thi. Thread chính là đoạn mã thực thi của một chương trình nên các chương trình đều có cơ hội thực thi.
Khi winsock được thiết kế lần đầu tiên, các mô hình thiết kế được làm cho phù hợp với cơ chế “ message-driven” và “ nonpreemptive” của Windows 16bits. Một số hàm socket nguyên thủy khi thực thi cần một khoảng thời gian tương đối. Khi hàm thực thi rơi vào tình trạng này, nó được gọi là bị tắc nghẽn. Khi một hàm bị tắc nghẽn, nó sẽ ngăn trở việc thực thi của các hàm khác trong hệ thống. Trong hệ thống UNIX, môi trường mà socket được thiết kế đầu tiên, các hàm blocking này không gây trở ngại cho hệ thống vì hệ thống sẽ chiếm giữ các quá trình bị blocking và cho phép các quá trình khác thực thi.
Trong khi đó, hệ thống Windows 16 bits không có khả năng chiếm giữ các quá trình blocking. Dẫn đến việc hệ thống không tiếp tục thực thi được vì các quá trình khác không có cơ hội thực thi. Hệ thống phải chờ cho đến khi quá trình blocking hoàn tất công việc thì mới tiếp tục thực thi được. Khi thiết kế winsock, các nhà thiết kế đã tính đến khả năng này. Vì vậy họ có một giải pháp là đưa một đoạn mã đặc biệt vào hàm blocking để cho phép các quá trình khác kiểm tra được hàng chờ Thông báo của mình. Tuy nhiên đây không phải là một giải thuật hiệu quả.
Trong hệ thống socket của Berkeley các nhà nghiên cứu cũng đã lưu ý đến vấn đề này khi thiết kế, và họ đã thiết kế các hàm nonblocking bên cạnh các hàm blocking. Chúng ta xét một ví dụ là hàm send() của socket. Khi hoạt động ở chế độ blocking, hàm send() sẽ gửi dữ liệu đi, hàm sẽ bị tắc nghẽn và nó chỉ trả về khi hoàn tất việc truyền dữ liệu, tức là dữ liệu đã được nhận hoàn toàn. Còn nếu socket được tạo ra ở cơ chế bất đồng bộ, hàm send() sẽ hoạt động ở chế độ non-blocking. Hàm send() sau khi gửi dữ liệu đi sẽ trả về ngay lập tức. Và hệ thống sẽ phải gọi một hàm khác như select() để quan sát tình trạng của việc gửi dữ liệu. Trên môi trường Windows chúng ta cũng có thể sử dụng các hàm non-blocking. Tuy nhiên các nhà thiết kế winsock còn đưa ra các hàm bất đồng bộ.[3]
Các hàm bất đồng bộ được đưa ra dựa trên cơ chế hoạt động message-driven của môi trường Windows. Chúng ta lấy ví dụ là các hàm gửi nhận dữ liệu. Việc gửi dữ liệu không nhất thiết phải diễn ra ngay lập tức, và việc nhận dữ liệu sẽ bắt buộc chương trình phải chờ trừ phi nó nhận được một hằng đặc biệt. Bằng cách tạo socket ở chế độ non-blocking để dùng các hàm non-blocking và kết hợp với hàm WSAAssyncSelect(), ứng dụng sẽ nhận được các message thông báo sự kiện để báo cho chương trình biết khi nào chương trình có thể gửi dữ liệu đi hoặc đã có dữ liệu truyền đến cần đọc ra từ socket. Trong các khoảng thời gian còn lại, khi không có thông báo các phần khác của hệ thống có thể thực thi được.
Các hàm bất đồng bộ rất phù hợp cho các hoạt động diễn ra trên môi trường Windows 16 bits là môi trường nonpreemptive. Trong môi trường Win32 như Windows NT hay Windows98 là môi trường preemptive các hàm blocking vẫn có thể sử dụng được. Tuy nhiên việc dùng các hàm bất đồng bộ trên môi trường Win32 giúp chương trình đáp ứng tốt hơn cho việc tương tác với người sử dụng. Một hàm blocking sẽ ngăn trở hệ thống đáp ứng kịp thời cho các thao tác của người sử dụng. Điều này rất quan trọng trên một môi trường giao diện như Windows. Vì vậy các hàm bất đồng bộ vẫn được sử dụng.
Vì môi trường Windows98 có hỗ trợ cơ chế lập trình song song thông qua việc định thời thực thi cho các thread, do đó trong việc thiết kế, chúng ta chọn dùng cơ chế blocking và thực hiện việc lập trình socket bằng các đối tượng do MFC cung cấp là các lớp CAsyncSocket, CSocket, CSocketFile, CArchive. Việc chọn lập trình bằng công cụ này vì có nhữnh đặc điểm sau:
Các lớp đối tượng đều do MFC hỗ trợ, phù hợp với cấu trúc chương trình được xây dựng dựa trên các lớp đối tượng MFC. Ứng dụng được xây dựng trên các lớp đối tượng MFC bằng các công cụ AppWizard, ClassWizard. Việc viết ứng dụng sẽ dễ dàng và đơn giản hơn. Và khi ứng dụng có hỗ trợ socket thông qua các lớp đối tượng socket của MFC ở trên, việc lập trình sẽ trở nên tiên lợi hơn.
Việc lập trình socket trên các lớp đối tượng thường dễ dàng và đơn giản hơn so với việc lập trình bằng các hàm socket nguyên thủy được hỗ trợ bởi Windows SDK. Chúng ta lấy một ví dụ như sau: tạo một socket và lắng nghe ở một port xác định.[6]
Lập trình bằng công cụ do Windows SDK hỗ trợ:
// Tạo Socket
SOCKET hSocket = socket ( int af = PF_INET, int type = SOCK_STREAM,
int protocol = 0);
// Ràng buộc socket vào một port cố định
SOCKADDR_IN sin;
u_short alport = IPPORT_RESERVED;
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = 0;
for (;;) {
sin.sin_port = htons(alport);
if (bind(hSocket, (LPSOCKADDR)&sin, sizeof (sin)) == 0){
/* it worked */
}
if ( GetLastError != WSAEADDRINUSE) {
/* fail */
}
alport--;
if (alport == IPPORT_RESERVED/2 )
{
/* fail--all unassigned reserved ports are */
/* in use. */
}
}
// Lắng nghe lời gọi liên kết
int listen ( hSocket, int backlog = 5 );
Trong khi đó nếu chúng ta lập trình bằng các lớp socket do MFC hỗ trợ thì các công việc phải thực thi như sau:
// Tạo lớp đối tượng
CSocket* m_pSocket = new CSocket;
// Tạo socket và ràng buộc vào một port xác định
m_pSocket->Create(nPort);
// Lắng nghe lời gọi liên kết
m_pSocket->Listen();
Khi lập trình chúng ta chọn các hàm blocking vì chúng được hỗ trợ bởi công cụ lập trình serialize. Cơ chế serialize cho phép hệ thống đảm bảo việc truyền nhận dữ liệu trên socket. Việc lập trình socket thông qua cơ chế serialize cũng đơn giản và dễ dàng hơn. Vì chương trình được hiện thực trên môi trường Windows98 cho nên chúng ta sẽ thực thi vào các hàm truyền nhận của socket. Vì vậy các hàm blocking cũng không ảnh hưởng nhiều đến việc thực thi của chương trình cũng như trong toàn hệ thống.
Ngoài ra việc dùng cơ chế Serialize (Serialization là một quá trình đọc một đối tượng dữ liệu từ đĩa hay ngược lại, ghi chúng lên dĩa. MFC hỗ trợ cơ chế serialization trong class object vì vậy bất cứ đối tượng nào dẫn xuất từ object đều thừa hưởng cơ chế serialization) cũng đồng nghĩa với việc chúng ta dùng giao thức TCP trong việc truyền nhận dữ liệu. Việc dùng giao thức này giúp quá trình trao đổi dữ liệu được diễn ra tin cậy hơn. Dĩ nhiên phí tổn hệ thống phải bỏ ra cũng cao hơn.
CHƯƠNG V
THIẾT KẾ CHƯƠNG TRÌNH
TRUYỀN ÂM THANH TRÊN MẠNG LAN
V.1 MÔI TRƯỜNG VÀ CÔNG CỤ LẬP TRÌNH
V.1.1 MÔI TRƯỜNG WINDOWS
Hệ điều hành Windows là hệ điều hành đa nhiệm , tức là nhiều chương trình có thể chạy một lúc. Trong môi trường đa nhiệm, chương trình có thể chia làm nhiều phần nhỏ gọi là các nhánh(thread) chạy đồng thời với nhau. Một chương trình khi chạy bao giờ cũng có một nhánh chính, sau đó chương trình có thể tạo ra các nhánh con bằng cách gọi đến các hàm CreateThread. Hệ điều hành sẽ làm nhiệm vụ điều khiển các nhánh này. Windows cho phép đặt các mức ưu tiên cho các nhánh. Nhánh luôn được khởi tạo ở mức ưu tiên bình thường, chương trình có thể thay đổi các mức ưu tiên của các nhánh bằng cách dùng hàm SetThreadPriority tuỳ theo mức yêu cầu. Có các mức ưu tiên sau đây :
Idle
Below Normal
Normal
Above Normal
High
Readtime
Mức ưu tiên dành cho các ứng dụng chỉ hoạt động khi hệ thống rỗi, mức cuối cùng dành cho các ứng dụng đòi hỏi thời gian thực.
IV.1.2 CÔNG CỤ LẬP TRÌNH
Do đặc điểm của chương trình là truyền nhậnh âm thanh trên mạng. Do đó lập trình liên qua rất nhiều đến winsock. Hiện nay có rất nhiều ngôn ngữ hỗ trợ cho việc lập trình trên mạng. Qua tìm hiểu em thấy rằng ngôn ngữ Visual C++ 6.0 có những hàm WinSock và những cấu trúc được khai báo trong WINSOCK.H cho cả môi trường 16 cũng như 32 bit. Những hàm Winsock được hiện thực trong WINSOCK.DLL (hay WSOCK32.DLL ứng với bản 32 bit). Chương trình ứng dụng sẽ được link với WINSOCK.LIB (hay WSOCK32.LIB). Chính vì vậy rất thích hợp cho lập trình các ứng dụng trên mạng.
Các Hàm TCP/IP
Sau đây là ý nghĩa chi tiết các tham số của các hàm TCP/IP. Để sử dụng các hàm này, trong chương trình cần có chỉ thị [6] :
# include
Hàm accept :
·Cú pháp:
SOCKET PASCAL FAR accept (
SOCKET s,
struct sockaddr FAR * addr,
int FAR &addrlen);
·Chức năng:
Xác nhận mối nối của client. Nếu thành công, sẽ trả về socket được nối vào, ngược lại trả về giá trị INVALID_SOCKET. Ý nghĩa các tham số :
Tham số
Ý nghĩa
S
Socket đang chờ client nối vào
Addr
Bộ đệm nhận về địa chỉ client nối vào
Addrlen
Chiều dài của địa chỉ trả về trong addr
Hàm bind :
·Cú pháp:
int PASCAL FAR bind (
SOCKET s,
const struct sockaddr FAR * name,
int namelen);
·Chức năng:
Kết buộc socket với port, đối với mỗi nghi thức ứng dụng chuẩn thì sẽ có một hằng số định nghĩa sẵn trong winsock cho port của nghi thức đó. Nếu thành công trả về 0, ngược lại trả về trị SOCKET_ERROR. Ý nghĩa các tham số :
Tham số
Ý nghĩa
S
Socket đang chờ kết buộc
Name
Địa chỉ port mà socket sẽ được kết buộc
Namelen
Chiều dài của tham số name
Hàm closesocket :
·Cú pháp:
int PASCAL FAR closesocket ( SOCKET s);
·Chức năng:
Sau khi hoàn tất quá trình trao đổi dữ liệu, ứng dụng gọi hàm closesocket để đóng socket đã tạo.
Hàm connect :
·Cú pháp:
int PASCAL FAR connect (
SOCKET s,
const struct sockaddr FAR * name,
int namelen);
·Chức năng:
Gọi hàm connect để nối client vào server. Nếu thành công trả về 0, ngược lại trả về giá trị SOCKET_ERROR. Ý nghĩa các tham số :
Tham số
Ý nghĩa
s
Socket đang chờ được nối với server
Name
Địa chỉ server sẽ nối
Namelen
Chiều dài của tham số name
Hàm inet_ntoa :
·Cú pháp:
char FAR* PASCAL FAR inet_ntoa (struct in_addr iadddr );
·Chức năng:
Nhận vào một địa chỉ internet iaddr và đổi thành địa chỉ internet dưới dạng chuỗi.
Hàm listen :
·Cú pháp:
int PASCAL FAR listen (
SOCKET s,
int backlog);
·Chức năng:
Tạo một hàng đợi tối đa backlog client trên port chờ được kết buộc với socket s.
Hàm recv :
·Cú pháp:
int PASCAL FAR revc (
SOCKET s,
char FAR* buf,
int len,
int flags);
·Chức năng:
Nhận dữ liệu từ socket. Sử dụng cho nghi thức TCP. Ý nghĩa các tham số :
Tham số
Ý nghĩa
s
Socket để nhận dữ liệu
buf
Bộ đệm chứa dữ liệu đọc được
len
Kích thước bộ đệm
flags
Cách thực hiện thao tác
Hàm recvfrom :
·Cú pháp:
int PASCAL FAR recvfrom( SOCKET s,
char FAR* buf,
int len,
int flags,
struct sockaddr* from,
int fromlen);
·Chức năng:
Nhận dữ liệu từ socket UDP, xác định địa chỉ của socket đã gửi dữ liệu. Ý nghĩa các tham số :
Tham số
Ý nghĩa
s
Socket để nhận dữ liệu
buf
Bộ đệm chứa dữ liệu đọc được
len
Kích thước bộ đệm
flags
Cách thực hiện thao tác, nhận giá trị MSG_PEEK hay MSG_OOB
from
Địa chỉ nơi dữ liệu được gửi đi
fromlen
Chiều dài của fromlen
Hàm send :
·Cú pháp:
int PASCAL FAR send (
SOCKET s,
const char FAR* buf,
int len,
int flags);
·Chức năng:
Gửi dữ liệu qua socket. Sử dụng cho nghi thức TCP .Ý nghĩa các tham số:
Tham số
Ý nghĩa
S
Socket để gửi dữ liệu
Buf
Bộ đệm chứa dữ liệu sẽ truyền
Len
Kích thước bộ đệm
Flags
Cach thực hiện thao tác
Hàm sendto :
·Cú pháp:
int PASCAL FAR sendto (
SOCKET s,
const char FAR* buf,
int len,
int flags,
const struct sockaddr *to,
int tolen);
·Chức năng:
Gửi dữ liệu đến một socket xác định. Sử dụng cho nghi thức UDP.Ý nghĩa các tham số:
Tham số
Ý nghĩa
s
Socket để gửi dữ liệu
buf
Bộ đệm chứa dữ liệu sẽ truyền
len
Kích thước bộ đệm
Flags
Cách thực hiện thao tác
to
Địa chỉ máy phải gửi dữ liệu
Tolen
Chiều dài của tolen
Hàm setsockopt :
·Cú pháp:
int PASCAL FAR setsockopt (
SOCKET s,
int level,
int optname);
const char FAR* optval,
int optlen);
·Chức năng:
Đặt các thuộc tính tùy chọn cho socket.Ý nghĩa các tham số như sau:
Tham số
Ý nghĩa
s
Socket cần được đặt thuộc tính
level
Mức định nghĩa thuộc tính, chỉ có thể nhận giá trị SOL_SOCKET hay IPPROTO_TCP
optname
Tên option muốn đặt, một số giá trị thông dụng :
-SO_BROADCAST: Cho phép truyền thông broadcast trên socket.
-SO_LINGER : Chờ trước khi đóng socket nếu còn dữ liệu chưa gửi.
Optval
Bộ đệm chứa giá trị sẽ gán cho option, có thể là BOOL hay struct, int tùy thuộc vào optname cụ thể
Optlen
Chiều dài của optlen
Hàm socket :
·Cú pháp:
SOCKET PASCAL FAR socket (
int addr,
int type,
int protocol);
·Chức năng:
Tạo một socket và trả về handle của socket nếu thành công, trường hợp có lỗi trả về giá trị INVALID_SOCKET.Ý nghĩa các tham số như sau:
Tham số
Ý nghĩa
addr
Đặc tả dạng lưu trữ địa chỉ, hiện nay chỉ có thể nhận giá trị AF_INET cho Internet
type
Qui định lựa chọn TCP hay UDP
SOCKET_STREAM: ứng với TCP
SOCKET_DGRAM : ứng với UDP
Protocol
Qui định nghi thức đặc biệt được dùng với socket, nhận giá trị 0 nếu không muốn sử dụng một nghi thức đặc biệt
Hàm WSAStartup :
·Cú pháp:
int PASCAL FAR WSAStartup(
WORD version,
LPWSADATA lpWSAData);
·Chức năng:
Đây là hàm phải gọi đầu tiên khi sử dụng socket. Hàm có nhiệm vụ khởi tạo thư viện liên kết động Windows socket DLL.Ý nghĩa các tham số như sau :
Tham số
Ý nghĩa
version
Version cao nhất của Windows sockets API có thể sử dụng
LpWSAData
Chỉ đến cấu trúc WSADATA nhận về chi tiết thực hiện của Windows Socket
Hàm WSACleanup :
·Cú pháp:
int PASCAL FAR WSACleanup(void);
·Chức năng:
Khi chấm dứt sử dụng các hàm socket phải gọi WSACleanup để đóng thư viện liên kết động Windows Socket DLL.
V.2 THIẾT KẾ CHƯƠNG TRÌNH CPHONE
Từ những lý thuyết ở trên em xin xây dựng chương trình CPhone với mục đích chính là truyền âm thanh trên mạng LAN như sau :
Công nghệ điện thoại ngày nay tuy phát triển mạnh và có nhiều phương pháp nén tín hiệu âm thanh có hiệu quả. Nhưng ở Việt Nam hiện nay mới chỉ bắt đầu đưa vào sử dụng. Trong điều kiện thời gian và trình độ có hạn nên đồ án này không thể đưa ra một mô hình điện thoại Internet hoàn chỉnh với đầy đủ các phương pháp nén tiếng nói hiệu quả mà chỉ đưa ra mô hình truyền nhận tiếng nói đơn giản nhất giữa hai thực thể máy tính.
V.2.1 MÔ HÌNH TRUYỀN ÂM THANH PC – PC TRÊN MẠNG
Việc xậy dựng một hệ thống sử dụng công nghệ điện thoại Internet theo mô hình thứ 2 và thứ 3 đã trình bầy trong phần mở đầu đòi hỏi quá trình nghiên cứu và thiết bị kỹ càng, đầu tư công nghệ, vốn thích đáng mới có thể thực hiện được. Trong đồ án này thực hiện theo mô hình thứ nhất là PC – PC, nó đơn giản hơn và không cầu đầu tư thiết bị mới, có thể tận dụng được các thiết bị có sẵn, có thể thiết kế. Thử nghiệm hoàn chỉnh trong phạm vì đồ án tốt nghiệp.
Mô hình PC – PC thức hiện hầu hết các thao tác bằng phần mềm, 2 máy tính chạy cùng một phần mềm như vậy có thể dễ dàng mở rộng dần từng bước, có thể thử nghiệm trên mạng LAN, sau đó có thể thử nghiệm trên mạng Internet.
Các bước cơ bản cho mô hình PC – PC gồm :
¢m thanh anolog từ microphone được chuyển thành digital tại soundcard(PCM,8kHz,8bits/mẫu).
Các mẫu được sao chép vào vùng đệm thành các khối có độ lớn nhất định.
Dùng các thuật nén PCM để nén các khối đó.
Thêm vào khối đã nến các thông tin khác
Gửi khối dữ liệu sau khi dòng goi đó qua socket(TCP/UDP)
Gói tín được truyền qua mạng vật lý đến đầu kia
Loại bỏ các thông tin thêm vào, giải nén các khối dữ liệu đó, ghi khối dữ liệu âm thanh được giải mã vào vùng đệm
Chép các mẫu trên vào vùng đệm của soundcard
Soundcard chuyển đổi các mẫu đó thành âm thanh.
V.2.2 XÂY DỰNG MÔ HÌNH CPHONE
Phần này trình bầy mô hình ứng dụng truyền âm thanh thoại cho phép trao đổi âm thanh trên mạng LAN, gọi tên là CPhone.
Hinh V.1 Cấu trúc của mô hình CPhone
Mô hình này gồm các yếu tố chính như sau:
Thiết bị ngoại vi và các kết nối vật lý (Physical transport) : Một số các thiết bị ngoại vi cần thiết như : micro, loa, sound card. Các thiết bị này đảm nhận việc thu , một phần của quá trình xử lý tiếng nói và phát tiếng nói. Các kết nối vật lý cung cấp phương tiện trên đó các bit dữ liệu được truyền. Kết nối vật lý CPhone trong mạng LAN, WAN, Internet có thể là cáp đồng trục, cáp quang, đường điện thoại v.v...
Giao thức liên lạc (TCP/IP) : Gồm một số các qui luật và nguyên tắc mà các thiết bị trên mạng có thể liên lạc và làm việc với nhau. Giao thức sử dụng các kết nối vật lý của mạng để truyền dữ liệu. Hệ thống CPhone sử dụng giao thức TCP/IP.
Hệ thống phần mềm : Phạm vị nghiên cứu của đồ án này thì chương trình được thiết kế có cấu hình tối thiểu là chỉ 2 máy tính cùng chạy một chương trình kết nối với nhau qua mạng. Tín hiệu âm thanh từ Microphone qua soundcard được mã hoá bằng phương pháp PCM,8kHz, 8bits/mẫu, được chia nhỏ thành từng đoạn có độ dài nhất định(frame), các frame này được mã hoá theo các thuật toán, cuối cùng gửi gói dữ liệu đã mã hoá đến socket của mạng và truyền gói này đến nơi nhận.
Theo các mô hình đưa ra ở trên và phạm vị nghiên cứu của đồ án này thì chương trình được thiết kế có cấu hình tối thiểu là chỉ 2 máy tính cùng chạy một chương trình kết nối với nhau qua mạng. Tín hiệu âm thanh từ Microphone qua soundcard được mã hoá bằng phương pháp PCM,8kHz, 8bits/mẫu, được chia nhỏ thành từng đoạn có độ dài nhất định(frame), các frame này được mã hoá theo các thuật toán, cuối cùng gửi gói dữ liệu đã mã hoá đến socket của mạng và truyền gói này đến nơi nhận.
Có thể chia thành 3 modul chính :
Modul xử lý âm thanh
Modul mã hoá(giải mã) âm thanh
Modul truyền nhận âm thanh.
Modul xử lý âm thanh
Modul mã hoá
(giải mã)
Modul truyền nhận
Mẫu tiếng nói thu được
Gói dữ liệu sau khi mã hoá
Mẫu tiếng nói đã giải mã
Gói dữ liệu nhận từ remote
Hình V.2 Các modul của chương trình CPhone
Khi chương trình này đưa vào thực tế sẽ góp phần vào giải quyết các công việc giữa các phòng ban trong cơ quan được nhanh chóng và thuận tiện. Mục đích cuối cùng mà chương trình có thể thực hiện được là có thể trực tiếp nói truyện với nhau trong mạng LAN của cơ quan hay công ty nào đó...
V.2.3 MODUL THU VÀ PHÁT ÂM THANH
Modul xử lý âm thanh làm nhiệm vụ giao tiếp với soundcard, ghi vào bộ nhớ và truyền cho modul II để mã hoá, đồng thời nhận các mẫu đã giải mã ở modul II để đưa ra soundcard trong quá trình nhận.
Quá trình thu và phát tiếng nói thông qua vùng đệm (buffer), có 2 loại vùng đệm trong Direct sound : Primary buffer và Secondary buffer. Primary buffer chứa tiếng nói mà người nghe sẽ nghe thấy. Chỉ có duy nhất một Primary buffer và buffer này do DirectSound tạo ra, secondary buffer chứa tiếng nói hoặc đoạn tiếng cần đưa ra. Chương trình có thể tạo ra một hoặc nhiều Secondary buffer. Khi phát tiếng nói trong secondary buffer, DerectSound sẽ chuyển tiếng nói sang Primary buffer và đưa ra soundcard. Chương trình ứng dụng có thể dùng DirectSoundCapture để ghi tiếng nói vào một Capturebuffer. Cũng như khi phát tiếng nói, chương trình có thể báo con trỏ đến một vị trí nào đó và khi nó đến cuối của buffer thì nó tự động quay lại vị trí đầu của buffer.
Khi thao tác với các buffer, DirectSound cho phép khó một phần của buffer lại để đảm bảo không bị ghi đè lên phần này.
Modul thu và phát tiếng nói nằm trong cùng một Record thread. Thread có mức ưu tiên cao nhất. Mỗi khi có một dòng thông báo từ DirectSound gửi đến tức là khi con trỏ Capture buffer đi đến vị trí quy định trước thì thread này sẽ được gọi đến. Lúc này, modul thu tiếng nói sẽ khoá phần Capture buffer vừa ghi xong. Néu trong buffer của modul phát tiếng nói có tín hiệu tiếng nói thì sẽ chép vào Secondary buffer của DirectSound.
Lưu đồ của modul thu và phát tiếng nói như sau :
Bắt đầu
Khoá buffer
DirectSound
Chép tín hiệu âm thanh vừa lấy mẫu vào Record buffer
Play buffer
có dữ liệu
Chép play buffer vào Secondary buffer
Mở khoá buffer DirectSound
Kích hoạt modul mã hoá
Kết thúc
đúng
sai
Hình V.3 Lưu đồ của modul thu và phát tiếng nói
V.2.4 MODUL MÃ HOÁ VÀ GIẢI MÃ ÂM THANH
Modul mã hoá được kích hoạt sau khi modul VAD xác định tín hiệu âm thanh có chức tiếng nói. Công việc của modul mã hoá gọi đến các thủ tục mã hoá tiếng nói(GSM,LPC…). Sau đó, chuyển dữ liệu đã mã hoá vào Send buffer tồi kích hoạt modul truyền. Modul nhận nằm trong thread mã hoá, có mức ưu tiên bình thường.
Bắt đầu
Kết thúc
Lấy một khung tiếng nói từ Record buffer
Play buffer
có dữ liệu
Mã hoá theo phương pháp PCM
Chép khung tiếng nói đã mã hoá vào Sendbuffer
Kích hoạt modul truyền
đúng
sai
Sau đây là lưu đồ giải thuật modul mã hoá :
Hình V.4 Lưu đồ giải thuật modul mã hoá
Modul giải mã tiếng nói được kích hoạt sau khi modul nhận được dữ liệu từ máy ở xa. Loại bỏ Header, các dữ liệu thừa và đưa vào trong buffer nhận. Modul giải mã sẽ xá định phương pháp giải mã tương ứng với phương pháp mã hoá được sử dụng ở bên truyền. Sau đó chuyển dữ liệu đã mã hoá vào buffer phát tiếng nói.
Bắt đầu
Kết thúc
Lấy một khung tiếng nói từ buffer nhận
Buffer nhận có dữ liệu
Mã hoá theo phương pháp PCM
Chép khung tiếng nói vào buffer phát
đúng
sai
Lưu đồ giải thuật modul giải mã như sau :
Hình V.5 Lưu đồ giải thuật modul mã hoá
V.2.5 MODUL TRUYỀN, NHẬN ÂM THANH
Modul này có nhiệm vụ giao tiếng giữa máy tính với mạng IP. Sử dụng giao thức TCP/IP.
Khi buffer truyền có dữ liệu cần truyền đi, modul truyền sẽ kích hoạt . dữ liệu cần truyền sẽ có thêm các header và các thông tin cần thiết khác. Modul truyền có mức ưu tiên bình thường.
Khi có dữ liệu đến, winsock sẽ gửi thông báo đến chương trình, chương trình nhận được thông báo và kích hoạt modul nhận. Modul nàu có nhiệm vụ nhận dữ liệu, kiểm tra, loại bỏ dữ liệu thừa cùng với header. Sau khi chuyển dữ liệu vào buffer nhận và kích hoạt modul giải mã.
Bắt đầu
Kết thúc
Lấy một khung tiếng nói từ buffer truyền
Buffer truyền có dữ liệu
Truyền theo giao thức TCP
đúng
sai
Lưu đồ giải thuật của modul truyền và nhận :
Hình V. 6 Lưu đồ giải thuật modul truyền
Bắt đầu
Kết thúc
Dùng giao thức TCP
Lấy gói tin
đúng
sai
Kích hoạt modul giải mã
Có goi tin chưa nhận
Hình V.7 Lưu đồ giải thuật modul nhận
V.3 THIẾT KẾ GIAO DIỆN CHƯƠNG TRÌNH CPHONE
Chương trình thực hiện với điều kiện hai máy đàm thoại phải cùng chạy chương trình ứng dụng này.
Giao diện chính của chương trình :
Hình V.8 Giao diện chương trình CPhone
Khi muốn bắt đầu cuộc đàm thoại, người sử dụng vào menu kết nối và click vào nhập IP. Điều này sẽ gọi hàm tạo kết nối giữa hai máy PC, hàm này hiển thị cửa sổ yêu cầu người sử dụng nhập vào IP của máy cần kết nối. Sau khi đã nhập xong click connect, một thông điệp yêu cầu kết nối sẽ gửi đến máy cần kết nối.
Hình V.9 Nhập địa chỉ IP
Nếu không có tín hiệu thì chưa cho phép quá trình đàm thoại. Khi có tín hiệu phản hồi thì các hàm khởi động sound card, card mạng và socket được gọi và thực hiện cuốc đàm thoại.
Hình V.10 Cuộc đàm thoại đang được thực hiện
IV.4 KẾT QUẢ THỰC NGHIỆM&NHẬN XÉT ĐÁNH GIÁ CHƯƠNG TRÌNH
IV.4.1 KẾT QUẢ THỰC NGHIỆM
Môi trường thử nghiệm đồ án này là mạng LAN, sử dụng công nghệ Ethernet 10 Mbps. Hiện tại truyền dữ liệu giữa các máy tính trong mạng LAN còn rất ít, phần lớn các tài nguyên về dải thông của mạng còn chưa sử dụng. Phần giải thông còn lại này là rất lớn để truyền tín hiệu tiếng nói, cũng vì lý do này nên độ trễ point – to – point trong mạng nhỏ và tương đối đồng đều (hầu hết < 10ms), giá trị này rất nhỏ so với mức chấp nhận được là 200ms. Vì chất lượng của cuộc đàm thoại trong trường hợp này hầu như chỉ phụ thuộc vào chất lượng của sound card.
IV.4.2 ĐÁNH GIÁ KẾT QUẢ
Qua thử nghiệm, em thấy chất lượng tiếng nói đầu ra không có gì khác biệt so với chất lượng tiếng nói đầu vào, chất lượng âm thanh sau khi truyền là có thể chấp nhận được. Như du sao đó chỉ là đánh giá qua cảm nhận của tai chúng ta mà thôi nên chưa thể khảng định được chất lượng tiếng nói sau khi truyền có đạt tiêu chuẩn cho phép hay không. Nếu sử dụng các máy đo các thông số liên quan đến chất lượng tiếng nói, phương pháp này đảm bảo đánh giá chính xác chất lượng tiếng nói trong điều kiện chấp nhận được. Nhưng luận văn này mới chỉ dừng lại ở mức độ đồ án tốt nghiệp và do điều kiện không cho phép nên chưa có thể có được các thiết bị đo các thông số của tiếng nói. Vì vậy, chưa thể đánh giá chất lượng âm thanh một cách chính xác được.
So với một số dịch vụ truyền âm thanh thoại hiện nay như : VoIP (dịch vụ 171) , Internet Telephony thì chất lượng âm thanh tốt hơn. Vì mô hình Cphone chỉ ứng dụng trên mạng cục bộ nên tốc độ đường truyền rất cao, mặt khác khoảng cách rất ngắn. Tuy nhiên độ trễ còn rất lớn song với mạng cục bộ thì vấn đề này vẫn có thể chấp nhận được. Còn đối với các dịch vụ 171 hay Internet Telephony hiện nay thì chất lượng âm thanh giai đoạn đầu nói chung không đạt được mong muốn đối với người sử dụng nhưng hiện tại chất lượng âm thanh đã được cải thiện đáng kể và đặc biệt các ứng dụng này ngày càng được đưa vào phục vụ cuộc sống.
KẾT LUẬN
Đồ án tốt nghiệp đã hoàn thành các nhiệm vụ đề ra trong phạm vi cho phép. Do điều kiện về thời gian cùng như trình độ còn hạn chế nên đồ án này dừng lại ở mức nghiên cứu một số phương pháp mã hoá tiếng nói và một số giao thức truyền tín hiệu trên mạng Internet như TCP/IP, UDP. Trên cơ sở đó xây dựng phần mềm thử nghiệm truyền tiếng nói thoại trên mạng LAN.
Đồ án này của em sử dụng các hàm có sẵn trong môi trường SDK từ đó xây dựng ứng dụng CPhone có thể liên kết hai máy lại và thực hiện quá trình trao đổi âm thanh.
Phần thử nghiệm mới chỉ dừng lại ở việc xây dựng một ứng dụng truyền tiếng nói giữa 2 thực thể PC theo giao thức TCP/IP và tiếng nói mới chỉ được mã hoá theo phương pháp PCM nên tỷ số nén và tốc độ truyền chưa cao. Mặt khác, do mới chỉ đánh giá được chất lượng tiếng nói theo phương pháp chủ quan nên chưa khảng định được điều gì về độ trễ và chất lượng tiếng nói sau khi truyền. Thế nhưng những gì đạt được trong đồ án này chủ yếu là nhằm giúp em có thể nắm bắt được vững hơn về mặt lý thuyết cũng như nắm quyền kiểm soát, quyền điều khiển ứng dụng trong thực tế, các kết quả này rất qua trọng và hữu ích cho mọi hoạt động nghiên cứu sau này cho dù những nội dung trong bài luận văn và phần thử nghiệm chưa được đầy đủ và hoàn chỉnh như mong muốn.
Hiện nay, mô hình truyền tiếng nói trên mạng(VoIP) đang được nghiên cứu và phát triển mạng trên thế giới nói chung và nước ta nói riêng, nhưng đây vẫn là một vấn đề rất mới, đặc biệt hiện này đã ứng dụng được cho mạng Internet. Qua thử nghiệm cho thấy hướng nghiên cứu có thể tiếp tục nâng cấp để đưa vào sử dụng, trước tiên với mô hình nhỏ là dùng cho mạng cục bộ trong các phòng ban. Từ đó có thể phát triển và đưa vào sử dụng trên diện rộng nhăm tăng chất lượng các cuộc đàm thoại, giảm giá thành cuộc gọi và để thương mại hoá sản phẩm. Về lâu dài thì có thể nghiên cứu, xây dựng các bộ giải mã cho ngôn ngữ tiếng việt; nghiên cứu ghép thêm các modul nhằm tạo các cuộc đàm thoại mật để đảm bảo an toàn cuộc gọi.
PHẦN PHỤ LỤC
Các từ viết tắt, ký hiệu :
AbS : Analysis by Synthesis
ADPCM : Adaptive Differential Pulse Code Modulation
ATC : Adaptive Transform Coding
PABX : Private Automatic Branch eXchange
PCM : Pulse Code Modulation
GMS : Global System for Mobile telecommucations
ETSI : European Telecommunication Standards Institude
GSM : Global System for Mobile telecommunication protocol
MCI : Media Control Interface
TCP : Transmission Control Protocol
UDP : User Datagram Protocol
IP : Internet protocol
RIFF : Resource Interchange File Format
IMA : Interactive Multimedia Association
ICMP : Internet Control Message Protocol
LPC : Linear Prediction Coding
OSI : Open System Interconnection
Chương trình nguồn :
Đoạn chương trình thực hiên chức năng đợi (chạy nền)
UINT Server(LPVOID param)
{
const PORT=1967;
const NO_FLAGS_SET=0;
const MAXBUFLEN=500;
WSADATA Data;
SOCKADDR_IN SvrAddr,CIntAddr;
SOCKET SvrSock,CIntSock;
int AddrLen=sizeof(SOCKADDR_IN);
int status,nbyte,nbsend;
char bufferrecv[MAXBUFLEN],buffersend[80];
//Khởi tạo Windows Socket DLL
status=WSAStartup(MAKEWORD(1,1),&Data);
if(status!=0)
{
MessageBox(NULL,"ERROR: WSAStartup unsuccessful.",
"WSAStartup error!",MB_ICONEXCLAMATION);
return(-1);
}
memset(&SvrAddr,0,sizeof(SvrAddr));
SvrAddr.sin_port=htons(PORT);
SvrAddr.sin_family=AF_INET;
SvrAddr.sin_addr.s_addr=htonl(INADDR_ANY);
SvrSock=socket(AF_INET,SOCK_STREAM,0);
if(SvrSock==INVALID_SOCKET)
{
MessageBox(NULL,"ERROR: Socket unsuccessful.","Socket error !",
MB_ICONEXCLAMATION);
WSACleanup();
return(-1);
}
//Kết buộc socket với PORT
bind(SvrSock,(LPSOCKADDR)&SvrAddr,sizeof(SvrAddr));
//Chờ client nối vào
listen(SvrSock,1);
//Xác nhận mối nối khi có client nối vào
CIntSock=accept(SvrSock,(LPSOCKADDR)&CIntAddr,&AddrLen);
if(CIntSock==INVALID_SOCKET)
{
MessageBox(NULL,"ERROR: Connect unsuccessful.","Connect error !",
MB_ICONEXCLAMATION);
closesocket(CIntSock);
WSACleanup();
return(-1);
}
else
{
nbyte=recv(CIntSock,bufferrecv,MAXBUFLEN,NO_FLAGS_SET);
if((nbyte==0)||(nbyte==SOCKET_ERROR))
{
MessageBox(NULL,"Please the end conversation !","The conversation !",MB_ICONEXCLAMATION);
closesocket(CIntSock);
WSACleanup();
return(-1);
}
else
MessageBox(NULL,bufferrecv,"The coversation !",MB_ICONINFORMATION);
strcpy(buffersend,"Receivered the signal. Please begin the conversation.");
nbsend=send(CIntSock,buffersend,strlen(buffersend)+1,NO_FLAGS_SET);
if(nbsend!=(int)strlen(buffersend)+1)
{
closesocket(CIntSock);
WSACleanup();
return(0);
}
return(0);
}
}
Đoạn mã thực hiện kết nối hai máy :
// Tạm dừng trạng thái chờ
SuspendThread(Server);
// Chuyển sang trạng thái gọi
m_DestAddrStr=dialog.m_DestAddrStr;
if(m_DestAddrStr=="")
{
MessageBox("ERROR: Hostname is unknown !","Error hostname !",
MB_ICONEXCLAMATION);
return(-1);
}
const PORT=1967;
const NO_FLAGS_SET=0;
const MAXBUFLEN=256;
WSADATA Data;
SOCKADDR_IN SockAddr;
unsigned long DestAddr;
int status,nbyte,nbrecv;
char SendBuf[50],RecvBuf[MAXBUFLEN];
// Khởi tạo Windows Socket DLL
status=WSAStartup(MAKEWORD(1,1),&Data);
if(status!=0)
{
MessageBox("ERROR :WSAStartup unsuccessf",
"Error WSAStarup !",MB_ICONEXCLAMATION);
return(-1);
}
// Nhận địa chỉ internet, chuyển sang dạng 4 byte
DestAddr=inet_addr(m_DestAddrStr);
//Khởi tạo cấu trúc SOCKADDR_IN
SockAddr.sin_addr=*(struct in_addr*)&DestAddr;
SockAddr.sin_port=htons(PORT);
SockAddr.sin_family=AF_INET;
// Tạo một socket
hSock=socket(AF_INET,SOCK_STREAM,0);
if(hSock==INVALID_SOCKET)
{
MessageBox("ERROR: Socket unsuccessful.","Error Socket",
MB_ICONEXCLAMATION);
WSACleanup();
return(-1);
}
// Nối vào máy chạy ứng dụng server
status=connect(hSock,(LPSOCKADDR)&SockAddr,sizeof(SockAddr));
if(status==SOCKET_ERROR)
{
MessageBox("ERROR: Connect unsuccessful !","Error connect !",
MB_ICONEXCLAMATION);
closesocket(hSock);
WSACleanup();
return(-1);
}
else
{
strcpy(SendBuf,"There is the signal send to your computer !");
nbyte=send(hSock,SendBuf,strlen(SendBuf)+1,NO_FLAGS_SET);
nbrecv=recv(hSock,RecvBuf,MAXBUFLEN,NO_FLAGS_SET);
if((nbrecv==0)||(nbrecv==SOCKET_ERROR))
{
MessageBox("Please end the conversation !", "The conversation",MB_ICONEXCLAMATION);
closesocket(hSock);
WSACleanup();
return(-1);
}
else MessageBox(RecvBuf,"The conversation",MB_ICONINFORMATION);
}
// Kết thúc trạng thái gọi
// Khôi phục lại trạng thái chờ
ResumeThread(Server);
return(1);
}
Đoạn chương trình thực hiện chức năng ghi âm thanh từ micro
void CWAveIn::recordWaveData()
{
HWAVEIN hWaveIn;
HWND hwndApp=NULL;
HGLOBAL hWaveHdr;
LPWAVEHDR lpWaveHdr;
HMMIO hmmio=NULL;
UINT wResult;
HANDLE hFormat=NULL;
WAVEFORMAT pFormat;
DWORD dwDataSize=1000;
// Mở thiết bị wave để phát.
if (!waveInOpen((LPHWAVEIN)&hWaveIn, WAVE_MAPPER,
(LPWAVEFORMATEX)&pFormat,
(LONG)waveInProc, DWORD(this), CALLBACK_FUNCTION))
{
MessageBox(
"Failed to open waveform input device.",
NULL,MB_OK | MB_ICONEXCLAMATION);
LocalUnlock(hFormat);
LocalFree(hFormat);
mmioClose(hmmio, 0);
return;
}
// Allocate and lock memory for the waveform data.
hData = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, dwDataSize );
if (!hData)
{
MessageBox( "Out of memory.",
NULL, MB_OK | MB_ICONEXCLAMATION);
mmioClose(hmmio, 0);
return;
}
if ((lpData =(HPSTR)GlobalLock(hData)) == NULL)
{
MessageBox( "Failed to lock memory for data chunk.",
NULL, MB_OK | MB_ICONEXCLAMATION);
GlobalFree(hData);
mmioClose(hmmio, 0);
return;
}
// Allocate and lock memory for the header.
hWaveHdr = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE,
(DWORD) sizeof(WAVEHDR));
if (hWaveHdr == NULL)
{
GlobalUnlock(hData);
GlobalFree(hData);
MessageBox("Not enough memory for header.",
NULL, MB_OK | MB_ICONEXCLAMATION);
return;
}
lpWaveHdr = (LPWAVEHDR) GlobalLock(hWaveHdr);
if (lpWaveHdr == NULL)
{
GlobalUnlock(hData);
GlobalFree(hData);
MessageBox(
"Failed to lock memory for header.",
NULL, MB_OK | MB_ICONEXCLAMATION);
return;
}
// After allocation, set up and prepare header.
lpWaveHdr->lpData = lpData;
lpWaveHdr->dwBufferLength = dwDataSize;
lpWaveHdr->dwFlags = 0L;
lpWaveHdr->dwLoops = 0L;
waveInPrepareHeader(hWaveIn, lpWaveHdr, sizeof(WAVEHDR));
// Gởi khối dữ liệu đến thiết bị phát.
wResult = waveInAddBuffer(hWaveIn, lpWaveHdr, sizeof(WAVEHDR));
if (wResult = 0)
{
waveInUnprepareHeader(hWaveIn, lpWaveHdr,
sizeof(WAVEHDR));
GlobalUnlock( hData);
GlobalFree(hData);
MessageBox( "Failed to write block to device",
NULL, MB_OK | MB_ICONEXCLAMATION);
return;
}
wResult = waveInStart(hWaveIn);
if (wResult != 0)
return;
}
Đoạn chương trình play dữ liệu âm thanh
void CWAveIn::playbackWaveData()
{
HWAVEOUT hWaveOut;
HWND hwndApp =NULL;
HGLOBAL hWaveHdr;
LPWAVEHDR lpWaveHdr;
HMMIO hmmio=NULL;
UINT wResult;
HANDLE hFormat=NULL;
WAVEFORMAT pFormat;
DWORD dwDataSize=1000;
// Mở thiết bị wave để phát.
if (waveOutOpen((LPHWAVEOUT)&hWaveOut, WAVE_MAPPER,
(LPWAVEFORMATEX)&pFormat,
(LONG)hwndApp, 0L, CALLBACK_WINDOW))
{
MessageBox(
"Failed to open waveform output device.",
NULL,MB_OK | MB_ICONEXCLAMATION);
LocalUnlock(hFormat);
LocalFree(hFormat);
mmioClose(hmmio, 0);
return;
}
// Allocate and lock memory for the waveform data.
hData = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, dwDataSize );
if (!hData)
{
MessageBox( "Out of memory.",
NULL, MB_OK | MB_ICONEXCLAMATION);
mmioClose(hmmio, 0);
return;
}
if ((lpData =(HPSTR)GlobalLock(hData)) == NULL)
{
MessageBox( "Failed to lock memory for data chunk.",
NULL, MB_OK | MB_ICONEXCLAMATION);
GlobalFree(hData);
mmioClose(hmmio, 0);
return;
}
// Allocate and lock memory for the header.
hWaveHdr = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE,
(DWORD) sizeof(WAVEHDR));
if (hWaveHdr == NULL)
{
GlobalUnlock(hData);
GlobalFree(hData);
MessageBox("Not enough memory for header.",
NULL, MB_OK | MB_ICONEXCLAMATION);
return;
}
lpWaveHdr = (LPWAVEHDR) GlobalLock(hWaveHdr);
if (lpWaveHdr == NULL)
{
GlobalUnlock(hData);
GlobalFree(hData);
MessageBox(
"Failed to lock memory for header.",
NULL, MB_OK | MB_ICONEXCLAMATION);
return;
}
// After allocation, set up and prepare header.
lpWaveHdr->lpData = lpData;
lpWaveHdr->dwBufferLength = dwDataSize;
lpWaveHdr->dwFlags = 0L;
lpWaveHdr->dwLoops = 0L;
waveOutPrepareHeader(hWaveOut, lpWaveHdr, sizeof(WAVEHDR));
// Gởi khối dữ liệu đến thiết bị phát.
wResult = waveOutWrite(hWaveOut, lpWaveHdr, sizeof(WAVEHDR));
if (wResult != 0)
{
waveOutUnprepareHeader(hWaveOut, lpWaveHdr,
sizeof(WAVEHDR));
GlobalUnlock( hData);
GlobalFree(hData);
MessageBox( "Failed to write block to device",
NULL, MB_OK | MB_ICONEXCLAMATION);
return;
}
}
TÀI LIỆU THAM KHẢO
[1]. ANNEXE VOIP-SLIDE
Raj Jain
[2]. AUDIO COMPRESSION THEOGRY
[3]. BUILDING INTERNET APPLICATION WITH VISUAL C++
Kate Gregogy - Paul Robichaux - Brady Merkel - Markus Pope
[4]. DIGITAL SIGNAL PROCESSING
John G.Proakis - Dimitris G.Manolakis - Prentice Hall
[5]. INTERNET WORKING VỚI TCP/IP
Nhà xuất bản giáo dục, 2001
[6]. Thư viện MSDN
Các file đính kèm theo tài liệu này:
- Xây dựng thử nghiệm một hệ thống cho phép trao đổi thông tin bằng tiếng nói thoại, tương tác điểm – điểm trên mạng LAN.doc