Thứ Bảy, 9 tháng 8, 2014

[JAIN SIP] Chương 9: Giới thiệu Java Media Framework (JMF)

Chương 9: Giới thiệu Java Media Framework (JMF)
Để thực hiện tính năng media được miêu tả trong SDP, chúng ta phải sử dụng công cụ lập trình media, có nhiều công cụ về lập trình media, ở đây chúng tôi sử dụng Java Media Framework (JMF). Nhưng chúng ta không tìm hiểu sâu JMF, chỉ tìm hiểu sơ về 1 số tính năng đủ để đáp ứng cho tính năng voice và video chat.
I. Media Stream là gì ?:
Mọi dữ liệu có ý nghĩa thay đổi theo thời gian đều có thể được mô tả dưới dạng media data,  như : các đoạn âm thanh, hình ảnh,…Các media data có thể có được từ nhiều nguồn khác nhau có sẵn trong máy tính, hay trên Internet, thu được từ camera, microphone, từ TV hay radio.
Media stream là sự biểu diễn media data dưới dạng stream (luồng). Các Media stream thường chứa nhiều channel (kênh) được gọi là các track. Ví dụ, Quicktime file chứa audio track và video track (audio track : thực hiện audio, video track : thực  hiện video). Media stream chứa nhiều track được gọi là multiplexed hay complex media stream (media stream đa hợp).
Một loại track nhận dạng một loại dữ liệu mà nó chứa như audio, video. Sự định dạng một track (còn gọi là media format) định nghĩa dữ liệu dành cho track được xây dựng như thế nào. Ví dụ, track sẽ chứa dữ liệu gì thì được xem là audio, còn track chứa dữ liệu gì thì xem là video.      
Media stream có thể được xác định vị trí và giao thức sử dụng để truy cập nó. Ví dụ: một tập tin trên máy tính có thể được truy cập qua giao thức File (File://). Tập tin trên một web server có thể được truy cập qua giao thức HTTP (http://). Như vậy, giao thức File và HTTP được sử dụng dưới dạng đường dẫn URL. Đối với một số thiết bị hay giao thức khác như RTP hay SoundCard, không thể sử dụng đường dẫn URL, chúng ta sử dụng media locator để cung cấp một cách khác để xác định vị trí của media stream, như : “rtp://”, “dsound://”,…
II. Giới thiệu JMF:
JMF là 1 Java API để thực hiện media trong lập trình Java. Nó cho phép lập trình viên phát triển Java Code để chụp, trình diễn, lưu giữ và xử lý media. Mặt khác, JMF cũng định nghĩa RTP API cho phép truyền và nhận RTP stream.
JMF cung cấp ba giai đoạn xử lý dữ liệu : Input (đầu vào), processing (xử lý) và output (đầu ra).
- Giai đoạn Input : bắt media stream. Media stream có thể có được từ các nguồn khác nhau như:
+ Từ các thiết bị bắt media (như microphone, camera,…).
+ Từ tập tin (như tập tin wav, mp3…).
+ Từ mạng (như nhận RTP Stream,… ).
-  Giai đoạn Processing: giai đoạn này lấy media stream có được ở giai đoạn Input và xử lý nó như:
+ Multiplexing/demultiplexing.
+ Encoding/decoding.
+ Packetizing/depacketizing.
- Giai đoạn Output: nhiệm vụ của giai đoạn này là truyền media stream đến điểm đến mong muốn. Các điểm đến có thể là:
+ Thiết bị trình diễn media (như loa, speaker,…).
+ Lưu dưới dạng tập tin.
+ Mạng (như truyền media stream dưới dạng 1 RTP Stream).
JMF cung cấp 2 dạng API. High-level API quản lý chụp, trình diễn và xử lý dữ liệu media. Low-level API (còn gọi là JMF plug-in API) hổ trợ mở rộng xử lý các thành phần như Denux, Codec, Effect,... Nhưng ở đây chúng ta chỉ tập trung vào high-level API.
P10_JMFAPI.png
Download JMF : http://www.oracle.com/technetwork/java/javase/download-142937.html. Phiên bản hiện tại của JMF là 2.1.1.e, phiên bản này chưa hổ trợ tốt cho các phiên bản 64bit JDK/JRE và tính năng video thường bị lỗi khi sử dụng hệ điều hành window từ Vista trở lên. Do đó, để cài đặt được JMF, tốt nhất nên sử dụng 32bit JDK/JRE và các bạn xài hệ điều hành window từ Vista trở lên, không thể chạy code về video trong quyển sách này. Do đó, chúng tôi sẽ hạn chế sử dụng tính năng video của JMF.
III. Các thực thể của JMF:
JMF API định nghĩa 1 vài thực thể mà mô phỏng quá trình xử lý media. Các thực thể đó:
+ Managers
+ Data source
+ Player
+ Processor
+ Data sink
+ Session manager
Lưu ý : Chúng tôi chỉ tập trung vào SessionManager để áp dụng trong JAIN SIP, nên những thực thể khác chúng tôi chỉ giới thiệu sơ qua.
1. Managers:
Managers là những đối tượng trung gian được sử dụng để quản lý và khởi tạo các đối tượng chính của JMF như Player, Processor,…JMF có 4 class dành cho manager:
- Manager
- CaptureDeviceManager
- PackageManager
- PlugInManager
Chúng tôi chỉ tập trung vào 2 manager, là : Manager và CaptureDeviceManager class
a. Manager class:
Manager class quản lý việc xây dựng các đối tượng Player, Processor, DataSource và DataSink.
b. CaptureDeviceManager class :
CaptureDeviceManager class quản lý sự đăng ký các thiết bị media có sẵn trên máy. Mỗi thông tin thiết bị được biểu diễn bằng đối tượng CaptureDeviceInfo. Để lấy ra các đối tượng CaptureDeviceInfo nào đó, chúng ta sử dụng phương thức getDeviceList() với tham số đầu vào là một đối tượng Format. Phương thức này trả về danh sách các thiết bị có khả năng thực hiện media.
2. Data source:
Media data được xử lý bởi JMF có thể là file video trên máy tính, audio track trên CD, hay từ internet,…tất cả chúng được gọi chung là media stream hay media source. JMF cung cấp một phương pháp đồng nhất để miêu tả và quản lý sự truyền tải chúng thông qua DataSource class. Mỗi DataSource có thể quản lý một hay nhiều media stream.
DataSource cũng được xem là quản lý quá trình truyền tải media-content. Để tạo ra một DataSource yêu cầu chỉ ra vị trí media và giao thức mà media có thể được lấy từ đó. Lưu ý, tất cả các DataSource không thể tái sử dụng được. Ví dụ, 1 Datasource được sử dụng cho Player, sau đó không thể sử dụng cho Processor.  
DataSource được nhận dạng bởi MediaLocator hay URL. DataSource sử dụng một mảng byte là đơn vị truyền tải. Mỗi giá trị của mảng byte được gọi là 1 buffer.
Ví dụ: DataSource được lấy từ 1 file:
MediaLocator ml=new MediaLocator(“file://c:\\music.wav”);
DataSource ds = Manager.createDataSource(ml);
3. Media Format :
Bảng dưới đây cho biết các đặc điểm của từng loại media format. Khi sử dụng 1 media format, điều quan trọng phải chú ý đến các đặc điểm của format, môi trường thực hiện, và tính toán số người nghe. Ví dụ, nếu chúng ta sử dụng media qua web thì chúng ta cần quan tâm đến bandwidth, sử dụng media trên máy tính quan tâm đến CPU.
Media format của 1 đối tượng được biểu diễn bằng đối tượng Format trong JMF. Nó miêu tả tên format và kiểu dữ liệu dành cho format.
JMF API định nghĩa Format class để miêu tả sự định dạng media. AudioFormat và VideoFormat là 2 class được sử dụng nhiều nhất và kế thừa từ Format class. AudioFormat miêu tả các đặc tính dành cho audio format như : sample rate, bits per sample và number of channels. VideoFormat bao gồm thông tin liên quan đến video data.
Ví dụ: tạo ra 1 đối tượng AudioFormat, có các thông số sau:
- Kiểu payload : GSM.
- sample rate: 8000
- bits per sample: 8
- number of channels: 1
AudioFormat af = new AudioFormat(AudioFormat.GSM,8000,8,1);
 
4. Player :
Player có nhiệm vụ xử lý và biểu diễn media stream như xuất ra màn hình, loa,... Một DataSource được sử dụng để phân phát media stream đến Player.  Nó được mô phỏng bằng Player interface.
Ví dụ:
Player p = Manager.createPlayer(ds);
p.start();
Trước khi biễu diễn media stream, Player được tiến hành theo từng trạng thái cho đến khi đạt được trạng thái cuối cùng Started. Player có 6 trạng thái : Unreadlized, Realizing, Realized, Prefetching, Prefetched, Started.
Thứ tự thực hiện 6 trạng thái của Player:
Unrealized -> Realizing -> Realized -> Prefetching -> Prefetched -> Started
Player gởi TransitionEvents khi nó di chuyển từ trạng thái này sang trạng thái khác. ControllerListener interface cung cấp cho người lập trình để xác định Player đang ở trạng thái nào để thực hiện những tính năng mong muốn.
5. Processor :
Processor cũng được sử dụng để trình diễn media stream. Processor được xem là một kiểu Player đặc biệt cũng cung cấp sự điều khiển quá trình thực hiện trên media stream. Processor có tất cả tính năng của Player.
Trong quá trình biểu diễn media stream đến các thiết bị trình diễn, Processor có thể xuất ra media stream đó thông qua DataSource, vì thế nó có thể được trình diễn bởi một Player  khác và có thể được điều khiển bởi một Processor khác, hoặc truyền đến DataSink để lưu thành file hay truyền qua mạng chẳng hạn.
Processor được mô phỏng bằng Processor interface và kế thừa Player interface. Processor có đầu vào là một DataSource gọi là “Input DataSource” và cho ra một DataSource mới gọi là  “Output DataSource”.
Processor bổ sung thêm hai trạng thái so với Player, là : ConfiguringConfigured, hai trạng thái mới này xảy ra trước trạng thái Realizing.
+  Configuring : kết nối Datasource và truy cập thông tin định dạng của media stream.
+ Configured : Xác nhận đã định dạng được media stream và cho phép chuyển sang trạng thái tiếp theo.
Thứ tự thực hiện 8 trạng thái của Processor :
Unrealized -> Configuring -> Configured -> Realizing -> Realized -> Prefetching -> Prefetched -> Started
6. Data sink :
Data sink nhận vào media stream và gởi đến đích đến. Tức là Data sink có thể ghi media stream dưới dạng file hay gởi thông qua mạng. Data Sink được biễu diễn bằng DataSink interface.
Ví dụ:  tạo một DataSink có đầu vào là DataSource và MediaLocator.
DataSink dsink = Manager.createDataSink(ds,ml);
Để bắt đầu chuyển media stream đến đích đến, có 2 bước cần thực hiện:
- Gọi phương thức open() của DataSink để mở kết nối với đích đến được nhận biết trong MediaLocator:
dsink.open();
- Kế tiếp gọi phương thức start() để khởi tạo quá trình chuyển media stream:
dsink.start();
 7. SessionManager:
SessionManager quản lý quá trình gởi và nhận media stream trong môi trường mạng qua giao thức RTP. SessionManager được sử dụng thay thế DataSink trong môi trường mạng, bởi SessionManager cung cấp mức độ nâng cao về sự điều khiển RTP session hơn DataSink.
IV. Real-Time Transport Protocol (RTP) :
RTP là giao thức truyền real-time data qua mạng (real-time data là những loại dữ liệu được thay đổi theo thời gian thực, đây là một cách gọi khác của media stream). Nó độc lập, mặc dù  thường được sử dụng cùng với UDP.
RTP chia media stream thành các gói theo tiêu chuẩn định dạng dành cho RTP, các gói này được gọi là RTP data packet. RTP không bảo đảm thứ tự các gói đã gởi. Do đó, người nhận phải có trách nhiệm xây dựng lại các gói của người gởi và dò tìm các gói bị mất bởi thông tin được cung cấp trong packet header (chúng ta sẽ tìm hiểu các header của RTP data packet ở phía sau trong phần này).
RTP không cung cấp bất cứ cơ chế nào để bảo đảm thời gian chuyển dữ liệu hay các dịch vụ an toàn khác. Những điều này được bổ sung ở giao thức RTCP (RTP Control protocol). Mục đích của RTCP là cung cấp thông tin về chất lượng dịch vụ kết nối của RTP bằng cách nhận biết những người tham gia và những thông tin liên quan như số lượng các gói nhận được và gởi đi, thời gian nhận và thời gian gởi,…
Tóm lại, RTP và RTCP liên kết rất chặt chẽ với nhau – RTP truyền dữ liệu trong khi RTCP được dùng để nhận thông tin phản hồi về chất lượng dịch vụ.
a. RTP Session:
RTP Session là quá trình kết nối 1 nhóm các ứng dụng, tất cả đều giao tiếp thông qua giao thức RTP. 1 RTP Session được nhận biết bằng cặp địa chỉ IP và port:
- IP và Port thứ nhất dùng cho RTP packet
- IP và Port thứ hai dùng cho RTCP packet.
Mỗi media type có 1 session của riêng nó. Ví dụ đối với bất kỳ số lượng ứng dụng tham gia trong cuộc hội thảo qua video (bao gồm audio và video) thì nó sẽ chứa 2 session :  audio session và video session.
b. RTP Data Packet:
Media Stream dành cho RTP Session được gởi đi dưới dạng một chuỗi các RTP Data Packet. Một chuỗi data packet bắt nguồn từ 1 Source được xem là RTP Stream. Nghĩa là, Media Stream bắt nguồn từ 1 source sử dụng giao thức RTP thì được xem là RTP Stream, mỗi RTP Stream chứa nhiều RTP Data Packet. Mỗi RTP Data Packet chứa 2 phần, đó là : header và  payload.
Các header của RTP data packet:
c. RTCP Packet :
Được bổ sung vào RTP stream trong 1 RTP session, các RTCP packet được gởi định kỳ đến tất các bên tham gia session. RTCP packet chứa thông tin về chất lượng dịch vụ, thông tin về source của media, thống kê dữ liệu đã được truyền,…
Các kiểu  RTCP packet:
RTCP Packet là đa hợp, chứa ích nhất là 2 nhưng luôn luôn phải có kiểu Source Description.
d. RTP Applications:
RTP application chia làm 2 nhóm :
- RTP Servers : nhận dữ liệu từ mạng.
- RTP Clients : truyền dữ liệu qua mạng.
Một vài ứng dụng, cũng như phần mềm hội thảo trực tuyến, cả client và server đều có thể truyền và nhận dữ liệu.  
V. Tìm hiểu JMF RTP API :
JMF cho phép phát và truyền RTP Stream qua các API được định nghĩa trong các gói javax.media.rtp, javax.media.rtp.event, và javax.media.rtp.rtcp.
Chúng ta có thể biểu diễn hay lưu RTP Stream dưới dạng tập tin:
p10_1_RTPReception
Tương tự, chúng ta có thể truyền đi các media được bắt từ thiết bị hay tập tin.
p10_1_RTPTransmission
SessionManager interface là thực thể được sử dụng để quản lý và kết hợp RTP session trong Java. Nó quản lý các bên tham gia RTP session và media stream được truyền. Nó cung cấp các phương thức:
- Mở và đóng một RTP session.
- Tạo RTP stream để gởi đi.
- Thêm và xóa các participant (participant : là bên tham gia vào 1 RTP session).
- Thống kê RTP session.
- ….
1. RTP Stream :
RTP Stream cũng được gọi là RTP Data,  là quá trình gởi và nhận media qua giao thức RTP được quản lý bởi Session Manager. Nó được biễu diễn bằng RTPStream class. RTPStream class có 2 class con kế thừa và mỗi RTP stream có 1 data source kết hợp với nó :
- ReceiveStream : biểu diễn một RTP Stream nhận được từ mạng.
- SendStream : biễu diễn một RTP Stream gởi đi từ Processor hay Input DataSource.
Đối tượng ReceivieStream được tạo tự động bất cứ lúc nào SessionManager dò tìm được RTP  stream mới.
2. RTP Events:
Các RTP Event được định nghĩa trong gói javax.media.rtp.event. Những event này được sử dụng để báo cáo  trạng thái của RTP session và RTP streams.
Để nhận thông báo về các RTP event, chúng ta sử dụng RTP listener và đăng ký nó với session manager.  Có 4 loại RTP listener được định nghĩa dành cho SessionManager:
- SessionListener : nhận thông báo khi có sự thay đổi trạng thái của RTP session (như 1 participant được thêm vào, sự xung đột tên trong RTP session,…).
- SendStreamListener : Nhận các sự kiện được kết hợp với 1 SendStream (như timeouts, thay đổi Payload, 1 stream mới,…)
- ReceiveStreamListener: Nhận các sự kiện được kết hợp với 1 ReceiveStream (như timeouts, 1 stream mới,…)
- RemoteListener : nhận thông báo về các sự kiện được tạo ra bởi các participant trong 1 RTP session.
a. SessionListener:
Trong SessionListener có 2 loại event:
- NewParticipantEvent: thông báo 1 participant mới đã tham gia session.
- LocalCollisionEvent : thông báo Synchronization source của participant vừa được sử dụng
b. SendStreamListener:
Trong SendStreamListener có 5 loại event:
- NewSendStreamEvent :  một RTP Stream mới đã được tạo trong SessionManager.
- ActiveSendStreamEvent: cho biết dữ liệu truyền đi từ DataSource được sử dụng để tạo Send Stream đã bắt đầu.
- InactiveSendStreamEven : cho biết dữ liệu truyền đi từ DataSource được sử dụng để tạo Send Stream đã kết thúc.
-  LocalPayloadChangeEvent : Người gởi RTP stream đã thay đổi payload type của RTP Stream.
- StreamClosedEvent : RTP Stream đã được đóng trong SessionManager.
c. ReceiveStreamListener:
Trong ReceiveStreamListener có 7 event:
- NewReceiveStreamEvent: cho biết session manager đã tạo 1 Receive Stream mới.
- ActiveReceiveStreamEvent: dữ liệu truyền đi đã bắt đầu.
- InactiveReceiveStreamEvent: dữ liệu truyền đi đã kết thúc.
- TimeoutEvent :dữ liệu truyền đi đã hết hạn.
- RemotePayloadChangeEvent : cho biết format hay payload của Receive Stream đã thay đổi.
- StreamMappedEvent : đối tượng ReceiveStream đơn lẻ trước đó đã kết hợp với 1 Participant.
- ApplicationEvent : gói RTCP APP đã nhận được.
d. RemoteListener:
Trong RemoteListener có 3 event :
- ReceiveReportEvent : cho biết 1 RTP receiver report đã nhận được.
- SenderReportEvent : cho biết 1 RTP Sender report đã nhận được.
- RemoteCollisionEvent : 2 participant từ xa đã sử dụng SSRC (Synchronization Source).
3. Biểu diễn RTP Stream:
Biểu diễn một RTP stream nhận được bằng một Player. Chúng ta sử dụng MediaLocator để xây dựng Player. MediaLocator dành cho RTP session có dạng như sau:
trong đó:
- address:là địa chỉ IP của RTP Session. Nói cách khác, address là địa chỉ IP của người khởi tạo media stream.
- port : Cổng của RTP Session.
- content-type : định nghĩa kiểu dữ liệu như : video, audio, text,…
- ssrc : nơi phát hay nơi khởi tạo media.
- ttl : là số lượng các router mà các RTP data packet có thể đi qua. Mỗi lần đi qua 1 router, ttl sẽ giảm 1 và không truyền nữa nếu ttl=1 mặc dù chưa đến đích mong muốn. ssrc và ttl thì không bắt buộc.
Ví dụ : Xây dựng Player và kết nối 1 RTP stream trong RTP Session.
Nếu có nhiều RTP stream trong session, thì chúng ta cần sử dụng Session Manager. Session Manager sẽ thông báo bất cứ lúc nào 1 stream mới được bổ sung đến session và xây dựng  Player cho mỗi stream mới đó. Sử dụng Session Manager cũng cho phép bạn trực tiếp giám sát và quản lý session.
4. Sử dụng SessionManager truyền RTP Stream:
Sử dụng SessionManager để truyền RTP Stream từ một thiết bị bắt media (như webcam, micro,..), chúng ta phải thực hiện 5 bước sau:
- Bước 1: Tạo, khởi tạo, và bắt đầu 1 SessionManager dành cho RTP session.
- Bước 2: Xây dựng 1 Processor sử dụng DataSource thích hợp.
- Bước 3: thiết lập output format của Processor đến RTP format.
- Bước 4: truy xuất output DataSource từ Processor.
- Bước 5: gọi phương thức createSendStream() của session manager và truyền media stream.
VI. Thực hành với JMF:
1. Bắt media stream từ thiết bị:
Nếu muốn lấy 1 media stream từ một thiết bị như microphone hay camera chẳng hạn. Chúng ta  sử dụng Manager để tạo DataSource, có 2 cách lấy DataSouce từ thiết bị :
- Cách 1: Nếu chúng ta biết được MediaLocator của thiết bị, chúng ta có thể trực tiếp lấy DataSource từ nó. Ví dụ: “dsound://8000” đại diện một audio card có samples rate là 8.000 Hz.

MediaLocator ml= new MediaLocator(“dsound://8000”);
DataSource ds = Manager.createDataSource(ml);

Để biết được MediaLocator của thiết bị đang tồn tại trên máy tính, bạn sử dụng chương trình JMFStudio (có sẵn khi bạn cài đặt jmf-2_1_1e-windows-i586.exe).
+ Mở tập tin JMFStudio.exe :
+ Chọn File/preferences :
+ Chọn tab "Capture Devices" và click vào button "Delect Capture Device" để JMF dò tìm các thiết bị media của bạn :
Trong hình trên, địa chỉ MediaLocator của webcam là : "vfw://0"
- Cách 2 : lấy đối tượng CaptureDeviceInfo tương ứng với thiết bị dựa trên đối tượng Format. Bằng cách gọi phương thức getDeviceList() của đối tượng CaptureDeviceManager, với đầu vào là một đối tượng Format. Khi có đối tượng CaptureDeviceInfo, chúng ta lấy   MediaLocator từ nó.
Ví dụ:
Với ví dụ trên, CaptureDeviceInfo tương ứng với thiết bị là soundcard có định dạng audio là Linear , audio sampled 8000 hz và bits per sample là 8.  
2. Lấy media stream từ tập tin:
Bạn có 1 tập tin media, muốn được biểu diễn trong JMF. Trước tiên, bạn phải chuyển tập tin media đó thành media stream, bằng cách sử dụng đối tượng MediaLocator với đầu vào là địa chỉ của tập tin và tạo DataSource.
Ví dụ: tạo media stream từ tập tin music.wav, chúng ta làm như sau:
Lưu ý :Nếu tập tin được lưu giữ trên WebServer, sử dụng giao thức HTTP trỏ đến vị trí tập tin.
3. Trình diễn media:
Giả sử, bạn đã có sẵn 1 DataSource và muốn trình diễn media stream trong DataSource này. Cách đơn giản nhất là sử dụng Player :
4. Xử lý media stream :
Để có thể lưu media stream dưới dạng tập tin hay truyền media stream qua mạng, thì trước tiên chúng ta phải xử lý media stream.
Để có thể xử lý media stream, chúng ta cần :
- Một DataSource đầu vào.
- Một đối tượng Processor.
- Bước 1 : xác định media stream sẽ được lưu dưới dạng tập tin hay truyền qua mạng, để lựa chọn Descriptor phù hợp:
- Bước 2 : Định dạng audio hoặc video :
- Bước 3 : Tạo đối tượng ProcessorModel và Processor :
- Bước 4 : Xuất ra một DataSource, xem như quá trình xử lý media stream đã hoàn thành.
Lưu ý : DataSource này được gọi là DataSource đầu ra. Bạn sử dụng DataSource đầu ra này để lưu lại media stream dưới dạng tập tin hoặc truyền qua mạng.
5. Lưu  media stream dưới dạng tập tin:
Để lưu 1 media stream dưới dạng tập tin, chúng ta cần 2 thông tin sau:
- Một DataSource đầu ra từ quá trình xử lý media stream.
- Địa chỉ tập tin để lưu media stream.
Cách đơn giản nhất là sử dụng DataSink :
Ví dụ : Viết chương trình ghi âm 10s và được lưu lại dưới tập tin "abc.wav".
Code đầy đủ : http://www.mediafire.com/download/cjbkz9k9kld3c7r/recoredVoice.rar
6. Truyền media stream qua mạng sử dụng DataSink:
JMF RTP API cung cấp 2 cách để nhận và gởi RTP media từ mạng. Cách thứ nhất sử dụng DataSink và cách thứ 2 sử dụng SessionManager. Sử dụng DataSink là cách đơn giản nhất và khá tốt nếu chúng ta chỉ gởi duy nhất 1 media stream. Nếu muốn gởi nhiều hơn 1 media stream, chúng ta phải sử dụng SessionManager.
Để truyền 1 media stream qua mạng sử dụng DataSink, chúng ta cần 2 thông tin sau:
- Một DataSource đầu ra từ quá trình xử lý media stream.
- Địa chỉ IP và port của máy tính nhận media stream.
Ví dụ : Viết chương trình truyền media qua mạng.
Để nhận media stream, bạn sử dụng JMFStudio. Mở JMStudio , chọn File -> Open RTP Session, nhập vào địa chỉ IP và port. Bấm vào button "Open" để nhận media stream được gởi.
Code đầy đủ : http://www.mediafire.com/download/qa78197o92tyh1b/VoiceTransmitRTP.rar
7. Truyền nhận media stream qua mạng sử dụng SessionManager :
Không giống như DataSink, chỉ có thể truyền media stream theo 1 hướng từ client đến server. Session Manager cho phép client truyền media stream đến server và cũng cho phép server truyền ngược lại media stream đến client trong một RTP Session, như vậy cả client và server đều có thể truyền và nhận media streeam trong cùng một RTP Session. Đồng thời, Session Manager cũng quản lý các bên tham gia Session và các media stream được truyền đi.
a. Tìm hiểu một số class được sử dụng trong Session Manager:
*  SessionAddress class :
RTP Session được kết hợp với nhiều địa chỉ (2 đối với unicast, nhiều hơn với multicast). Những địa chỉ này địa diện cho các bên tham gia trong một RTP Session. Địa chỉ này bao gồm 2 cặp địa chỉ IP và Port :
+ Một cặp dành cho data  (RTP).
+ Cặp khác dành cho control (RTCP).  
JMF triển khai class SessionAddress để biểu biễn các địa chỉ trong RTP Session. Các phương thức khởi tạo dành cho class SessionAddress:
Vi dụ: tạo một SessionAddress
* SourceDescription class :
SourceDescription class chứa các mục tin của source description được sử dụng trong RTCP SDES reports. Những mục tin này bao gồm : CNAME, EMAIL, LOC, NAME, NOTE, PHONE, PRIV, TOOL. Trong môi trường multi session, bắt buộc bên nào truyền media stream phải miêu tả SourceDescription nhưng bắt buộc phải có mục tin CNAME với ý nghĩa là nhận biết bên nào đã truyền media. Ngược lại, trong môi trường point-to-point (unicast) không cần SourceDescription.
Ví dụ :
Trong đó:
- SourceDescription.SOURC_DESC_NAME là mục tin CNAME.
- KhangDang : là tên đặt cho mục tin CNAME.
- 1 : tần số xuất hiện.
- false : không cần mã hóa.
b. Thực hiện quá trình truyền media stream qua mạng sử dụng SessionManager :
Để truyền 1 media stream qua mạng sử dụng Session Manager:
- Phía client cần cung cấp 2 thông tin sau:
+ Một DataSource đầu ra từ quá trình xử lý media stream.
+ Địa chỉ IP và port của client nhận media stream.
- Phía server cũng cung cấp 2 thông tin sau:
+ Một DataSource đầu ra từ quá trình xử lý media stream (nếu muốn truyền media đến client).
+  Địa chỉ IP và port của server nhận media stream.
Thực hiện thứ tự 5 bước như sau:
* Bước 1: Tạo, khởi tạo, và bắt đầu 1 SessionManager dành cho RTP session.
- Sử dụng class con của SessionManager là RTPSessionMgr để tạo 1 RTP Session :
- Đăng ký sự kiện ReceiveStreamListener : Để bắt sự kiện khi server nhận được media stream từ client gởi đến:  
- Khởi tạo SessionManager bằng phương thức initSession(). Phương thức initSession() có 4 tham số đầu vào :
+ Đối tượng của class SessionAddress : Ở giai đoạn này, SessionManager cần 1 địa chỉ cục bộ (localAddress) để khởi tạo Session, với ý nghĩa thông báo máy tính này gia nhập vào Session. Bạn chỉ cần tạo ra 1 SessionAddress rỗng, tức là localAddress, còn port không cần thiết.
+ Mảng đối tượng SourceDescription : trong môi trường multicast, bạn phải định nghĩa ít nhất là một thuộc tính, đó là CNAME, với ý nghĩa máy tính có tên CNAME gia nhập Session. Ngược lại, trong môi trường unicast, tham số này không quan trọng nên bạn để là null.
+ Phần trăm của session bandwidth mà RTP Session sử dụng khi gởi đi RTCP reports là biến rtcp_bw_fraction kiểu double.
+ Phần trăm của rtcp_bw_fraction mà RTP Session sử dụng khi gởi đi RTCP Sender reports là biến rtcp_sender_bw_fraction kiểu double.
Lưu ý : chúng ta không tìm hiểu sâu về RTCP reports, nên chúng ta cứ sử dụng giá trị thông dụng dành cho :  rtcp_bw_fraction = 0.05 và rtcp_sender_bw_fraction = 0.25
- Bắt đầu SessionManager bằng phương thức startSession().
+ Trong môi trường multicast : tham số đầu vào là :
. Đối tượng SessionAddress chứa địa chỉ IP phải là địa chỉ multicast.
. Giá trị ttl kiểu int : tổng số router mà địa chỉ này được đi qua.
. Đối tượng EncryptionInfo : chỉ ra kiểu mã hóa dành cho Session như DES, MD5,... Nếu không cần thì chúng ta thiết lập nó là null.
Ví dụ :
+ Trong môi trường unicast : tham số đầu vào là :
. localReceiverAddress : là đối tượng SessionAddress chứa địa chỉ local và Port - địa chỉ nhận Data và Control packets .
. localSenderAddress : là đối tượng SessionAddress chứa địa chỉ local và Port - địa chỉ gởi Data và Control packets.
. remoteReceiverAddress: là đối tượng SessionAddress chứa địa chỉ IP và Port - địa chỉ người nhận Data và Control packets.
. Đối tượng EncryptionInfo : chỉ ra kiểu mã hóa dành cho Session như DES, MD5,... Nếu không cần thì chúng ta thiết lập nó là null.
Ví dụ :
* Bước 2: Xây dựng 1 Processor sử dụng DataSource thích hợp.
* Bước 3: thiết lập output format của Processor đến RTP format.
* Bước 4: truy xuất output DataSource từ Processor.
(Bước 2,3,4 là quá trình xử lý media stream - xem lại phần 4 của VI: "xử lý media stream")
* Bước 5: gọi phương thức createSendStream() của session manager và gởi media stream.
- Để gởi media stream, chúng ta gọi phương thức createSendStream() của Session Manager để tạo ra 1 đối tượng SendStream với tham số đầu vào outputDataSource và index chỉ ra vị trí RTP Stream được bắt đầu. Nếu index = 1, chỉ ra vị trí RTP Stream đầu tiên. Nếu index = 0 , trộn tất cả các RTP Stream thành 1 RTP Stream duy nhất. Sau đó, gọi phương thức start() của đối tượng SendStream để gởi đi media stream:
- Để nhận media stream, sử dụng phương thức update() của ReceiveStreamListener. ReceiveStreamListner sẽ bắt sự kiện khi server nhận được media stream được gởi từ client trong phương thức update().
VII. Sử dụng Swing và JMF tạo chương trình voice chat đơn giản.
Giả sử, máy tính A và máy tính B muốn thực hiện voice chat với nhau, thì A và B phải cùng mở ứng dụng giao diện trên và cung cấp địa chỉ IP và Port để nhận media stream. Nghĩa là, A phải biết địa chỉ IP và Port của B để có thể truyền media stream đến B, ngược lại B phải biết địa chỉ IP và Port của A để có thể truyền media stream đến A.
Hình trên cho thấy :
+ Địa chỉ IP và Port của A để nhận media stream là 192.168.100 và 40000
+ Địa chỉ IP và Port của B để nhận media stream là 192.168.100 và 50000
Vì chạy trên cùng 1 máy tính nên địa chỉ IP của A và B giống nhau, nhưng phải khác Port.
Chúng ta tạo ra 2 class :
+ ProcessVoiceChat class : xử lý và thực hiện tính năng voice chat.
+ GUIVoiceChat class : tạo các thành phần giao diện đồ họa như hình trên.
1. ProcessVoiceChat class :
ProcessVoiceChat class kế thừa ReceiveStreamListener để đăng ký sự kiện  ReceiveStreamListener bắt sự kiện khi server nhận được media stream từ client gởi đến, và bao gồm các phương thức tương ứng với các bước trong phần "thực hiện quá trình truyền media stream qua mạng sử dụng SessionManager".
- Phương thức init() : tạo, khởi tạo và bắt đầu SessionManager, gồm 3 tham số đầu vào :
+ String revIP : địa chỉ IP của người nhận.
+  int revPort : Port của người nhận.
+  int localPort : Port của người gởi.
- Phương thức startMedia() : Quá trình xử lý media stream.
- Phương thức send() : Truyền media stream đến người nhận .
- Phương thức stop() : kết thúc voice chat.
2. GUIVoiceChat class :
- Port of Receiver : Port của người nhận.
- IP of Receiver : địa chỉ IP của người nhận
-  LocalPort : Port của người gởi.
Giá trị của 3 TextField này là 3 tham số đầu vào khi gọi phương thức init() của ProcessVoiceChat.
- Button "VOICE CHAT" thực hiện voice chat giữa 2 máy. Khi button này được bấm, các phương thức init(), startmedia(), send() của ProcessVoiceChat lần lượt được gọi.
- Button END : kết thúc voice chat. Khi button này được bấm, phương thức stop() của ProcessVoiceChat được gọi.
3. Chạy chương trình :
Chạy 2 giao diện trong tập tin GUIVoiceChat, mỗi giao diện tương ứng với 1 máy tính. Khi nhập đầy đủ thông tin yêu cầu, bấm vào button "VOICE CHAT" để thực hiện cuộc gọi giữa 2 máy. 

Code đầy đủ : http://www.mediafire.com/download/l9sk97vtc152t6f/exampleRTPSESSION.rar

3 nhận xét:

  1. CÁI NÀY LÀ VIẾT BẰNG NETBEANS HAY BẰNG GÌ VẬY B,MÌNH K HIỂU LẮM ??

    Trả lờiXóa
  2. mình đang cần demo của cái này nếu b có cho mình xin với ( kaynguyen020798@gmail.com) cám ơn b

    Trả lờiXóa
  3. Cho e hỏi, ở chương trình thứ 2 (voiceTransmitRTP) mở bằng JMstudio không nhận được gì vậy a.

    Trả lờiXóa