锁定老帖子 主题:JMF视频音频通信( 图+源码 )
精华帖 (0) :: 良好帖 (1) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2011-06-20
最后修改:2011-06-20
原创文章,转载请标明出处!谢谢! 工程文件下载地址:http://download.csdn.net/source/3378150 本文地址: http://mzhx-com.iteye.com/blog/1098698 效果图如下:
三个类 源代码如下:
package vidioPlay; import java.awt.Dimension; import java.io.IOException; import java.net.InetAddress; import java.util.Vector; import javax.media.CaptureDeviceInfo; import javax.media.Codec; import javax.media.Control; import javax.media.Controller; import javax.media.ControllerClosedEvent; import javax.media.ControllerEvent; import javax.media.ControllerListener; import javax.media.Format; import javax.media.IncompatibleSourceException; import javax.media.Manager; import javax.media.MediaLocator; import javax.media.NoProcessorException; import javax.media.Owned; import javax.media.Player; import javax.media.Processor; import javax.media.cdm.CaptureDeviceManager; import javax.media.control.QualityControl; import javax.media.control.TrackControl; import javax.media.format.AudioFormat; import javax.media.format.VideoFormat; import javax.media.protocol.ContentDescriptor; import javax.media.protocol.DataSource; import javax.media.protocol.PushBufferDataSource; import javax.media.protocol.PushBufferStream; import javax.media.protocol.SourceCloneable; import javax.media.rtp.RTPManager; import javax.media.rtp.SendStream; import javax.media.rtp.SessionAddress; import javax.media.rtp.rtcp.SourceDescription; import javax.swing.JFrame; public class MediaTransmit { private String ipAddress; private int portBase; private MediaLocator audioLocator = null, vedioLocator = null; private Processor audioProcessor = null; private Processor videoProcessor = null; private DataSource audioDataLocal = null, videoDataLocal = null; private DataSource audioDataOutput = null, videoDataOutput = null; private RTPManager rtpMgrs[]; private DataSource mediaData = null; private DataSource dataLocalClone = null; private PlayPane playFrame; public MediaTransmit(String ipAddress, String pb) { this.ipAddress = ipAddress; Integer integer = Integer.valueOf(pb); if (integer != null) { this.portBase = integer.intValue(); } // ///////////////////////////////////////////// playFrame = new PlayPane(); JFrame jf = new JFrame("视频实例"); jf.add(playFrame); jf.pack(); jf.setLocationRelativeTo(null); jf.setDefaultCloseOperation(3); jf.setVisible(true); // //////////////////////////////////////////// Vector<CaptureDeviceInfo> video = CaptureDeviceManager .getDeviceList(new VideoFormat(null)); Vector<CaptureDeviceInfo> audio = CaptureDeviceManager .getDeviceList(new AudioFormat(AudioFormat.LINEAR, 44100, 16, 2)); // MediaLocator mediaLocator = new // MediaLocator("file:/C:/纯音乐 - 忧伤还是快乐.mp3"); if (audio != null && audio.size() > 0) { audioLocator = ((CaptureDeviceInfo) audio.get(0)).getLocator(); if ((audioProcessor = createProcessor(audioLocator)) != null) { audioDataLocal = mediaData; audioDataOutput = audioProcessor.getDataOutput(); } } else { System.out.println("******错误:没有检测到您的音频采集设备!!!"); } // ///////////////////////////////////////////////////////// if (video != null && video.size() > 0) { vedioLocator = ((CaptureDeviceInfo) video.get(0)).getLocator(); if ((videoProcessor = createProcessor(vedioLocator)) != null) { videoDataLocal = mediaData; videoDataOutput = videoProcessor.getDataOutput(); } } else { System.out.println("******错误:没有检测到您的视频设备!!!"); } // ///////////////////////////////////////////////////////// final DataSource[] dataSources = new DataSource[2]; dataSources[0] = audioDataLocal; dataSources[1] = videoDataLocal; try { DataSource dsLocal = Manager.createMergingDataSource(dataSources); playFrame.localPlay(dsLocal); } catch (IncompatibleSourceException e) { e.printStackTrace(); return; } // ////////////////////////////////////////////////远程传输 dataSources[1] = audioDataOutput; dataSources[0] = videoDataOutput; try { DataSource dsoutput = Manager.createMergingDataSource(dataSources); createTransmitter(dsoutput); } catch (IncompatibleSourceException e) { e.printStackTrace(); return; } audioProcessor.start(); videoProcessor.start(); } private Processor createProcessor(MediaLocator locator) { Processor processor = null; if (locator == null) return null; // 通过设备定位器得到数据源, try { mediaData = Manager.createDataSource(locator); // 创建可克隆数据源 mediaData = Manager.createCloneableDataSource(mediaData); // 克隆数据源,用于传输到远程 dataLocalClone = ((SourceCloneable) mediaData).createClone(); } catch (Exception e) { e.printStackTrace(); return null; } try { processor = javax.media.Manager.createProcessor(dataLocalClone); } catch (NoProcessorException npe) { npe.printStackTrace(); return null; } catch (IOException ioe) { return null; } boolean result = waitForState(processor, Processor.Configured); if (result == false) return null; TrackControl[] tracks = processor.getTrackControls(); if (tracks == null || tracks.length < 1) return null; ContentDescriptor cd = new ContentDescriptor(ContentDescriptor.RAW_RTP); processor.setContentDescriptor(cd); Format supportedFormats[]; Format chosen; boolean atLeastOneTrack = false; for (int i = 0; i < tracks.length; i++) { if (tracks[i].isEnabled()) { supportedFormats = tracks[i].getSupportedFormats(); if (supportedFormats.length > 0) { if (supportedFormats[0] instanceof VideoFormat) { chosen = checkForVideoSizes(tracks[i].getFormat(), supportedFormats[0]); } else chosen = supportedFormats[0]; tracks[i].setFormat(chosen); System.err .println("Track " + i + " is set to transmit as:"); System.err.println(" " + chosen); atLeastOneTrack = true; } else tracks[i].setEnabled(false); } else tracks[i].setEnabled(false); } if (!atLeastOneTrack) return null; result = waitForState(processor, Controller.Realized); if (result == false) return null; setJPEGQuality(processor, 0.5f); return processor; } private String createTransmitter(DataSource dataOutput) { PushBufferDataSource pbds = (PushBufferDataSource) dataOutput; PushBufferStream pbss[] = pbds.getStreams(); System.out.println("pbss.length:" + pbss.length); rtpMgrs = new RTPManager[pbss.length]; SendStream sendStream; int port; // SourceDescription srcDesList[]; for (int i = 0; i < pbss.length; i++) { try { rtpMgrs[i] = RTPManager.newInstance(); port = portBase + 2 * i; SessionAddress localAddr = new SessionAddress( InetAddress.getLocalHost(), port); SessionAddress destAddr = new SessionAddress( InetAddress.getByName(ipAddress), port); rtpMgrs[i].initialize(localAddr); rtpMgrs[i].addTarget(destAddr); System.out.println("Created RTP session: " + InetAddress.getLocalHost() + " " + port); sendStream = rtpMgrs[i].createSendStream(dataOutput, i); sendStream.start(); } catch (Exception e) { e.printStackTrace(); return e.getMessage(); } } return null; } Format checkForVideoSizes(Format original, Format supported) { int width, height; Dimension size = ((VideoFormat) original).getSize(); Format jpegFmt = new Format(VideoFormat.JPEG_RTP); Format h263Fmt = new Format(VideoFormat.H263_RTP); if (supported.matches(jpegFmt)) { width = (size.width % 8 == 0 ? size.width : (int) (size.width / 8) * 8); height = (size.height % 8 == 0 ? size.height : (int) (size.height / 8) * 8); } else if (supported.matches(h263Fmt)) { if (size.width < 128) { width = 128; height = 96; } else if (size.width < 176) { width = 176; height = 144; } else { width = 352; height = 288; } } else { return supported; } return (new VideoFormat(null, new Dimension(width, height), Format.NOT_SPECIFIED, null, Format.NOT_SPECIFIED)) .intersects(supported); } void setJPEGQuality(Player p, float val) { Control cs[] = p.getControls(); QualityControl qc = null; VideoFormat jpegFmt = new VideoFormat(VideoFormat.JPEG); for (int i = 0; i < cs.length; i++) { if (cs[i] instanceof QualityControl && cs[i] instanceof Owned) { Object owner = ((Owned) cs[i]).getOwner(); if (owner instanceof Codec) { Format fmts[] = ((Codec) owner) .getSupportedOutputFormats(null); for (int j = 0; j < fmts.length; j++) { if (fmts[j].matches(jpegFmt)) { qc = (QualityControl) cs[i]; qc.setQuality(val); System.err.println("- Setting quality to " + val + " on " + qc); break; } } } if (qc != null) break; } } } private Integer stateLock = new Integer(0); private boolean failed = false; Integer getStateLock() { return stateLock; } void setFailed() { failed = true; } private synchronized boolean waitForState(Processor p, int state) { p.addControllerListener(new StateListener()); failed = false; if (state == Processor.Configured) { p.configure(); } else if (state == Processor.Realized) { p.realize(); } while (p.getState() < state && !failed) { synchronized (getStateLock()) { try { getStateLock().wait(); } catch (InterruptedException ie) { return false; } } } if (failed) return false; else return true; } class StateListener implements ControllerListener { public void controllerUpdate(ControllerEvent ce) { if (ce instanceof ControllerClosedEvent) setFailed(); if (ce instanceof ControllerEvent) { synchronized (getStateLock()) { getStateLock().notifyAll(); } } } } public static void main(String[] args) { String[] strs = { "localhost", "9994" }; new MediaTransmit(strs[0], strs[1]); } }
package vidioPlay; import java.awt.BorderLayout; import java.awt.Component; import java.awt.Dimension; import java.awt.Panel; import java.net.InetAddress; import java.util.Vector; import javax.media.ControllerErrorEvent; import javax.media.ControllerEvent; import javax.media.ControllerListener; import javax.media.Player; import javax.media.RealizeCompleteEvent; import javax.media.bean.playerbean.MediaPlayer; import javax.media.control.BufferControl; import javax.media.format.FormatChangeEvent; import javax.media.protocol.DataSource; import javax.media.rtp.Participant; import javax.media.rtp.RTPControl; import javax.media.rtp.RTPManager; import javax.media.rtp.ReceiveStream; import javax.media.rtp.ReceiveStreamListener; import javax.media.rtp.SessionListener; import javax.media.rtp.event.ByeEvent; import javax.media.rtp.event.NewParticipantEvent; import javax.media.rtp.event.NewReceiveStreamEvent; import javax.media.rtp.event.ReceiveStreamEvent; import javax.media.rtp.event.RemotePayloadChangeEvent; import javax.media.rtp.event.SessionEvent; import javax.media.rtp.event.StreamMappedEvent; import javax.swing.JFrame; import net.sf.fmj.media.rtp.RTPSocketAdapter; public class MediaReceive implements ReceiveStreamListener, SessionListener, ControllerListener { String sessions[] = null; RTPManager mgrs[] = null; boolean dataReceived = false; Object dataSync = new Object(); private PlayPane playFrame; public MediaReceive(String sessions[]) { this.sessions = sessions; } protected void initialize() { playFrame = new PlayPane(); JFrame jf = new JFrame("视频实例"); jf.add(playFrame); jf.pack(); jf.setLocationRelativeTo(null); jf.setDefaultCloseOperation(3); jf.setVisible(true); try { // 每一个session对应一个RTPManager mgrs = new RTPManager[sessions.length]; // 创建播放窗口的向量vector SessionLabel session = null; // Open the RTP sessions. // 针对每一个会话对象进行ip、port和ttl的解析 for (int i = 0; i < sessions.length; i++) { // Parse the session addresses. // 进行会话对象的解析,得到ip、port和ttl try { session = new SessionLabel(sessions[i]); } catch (IllegalArgumentException e) { System.err .println("Failed to parse the session address given: " + sessions[i]); // return false; } System.err.println(" - Open RTP session for: addr: " + session.addr + " port: " + session.port + " ttl: " + session.ttl); // 这对本条会话对象创建RTPManager mgrs[i] = (RTPManager) RTPManager.newInstance(); mgrs[i].addSessionListener(this); mgrs[i].addReceiveStreamListener(this); // Initialize the RTPManager with the RTPSocketAdapter // 将本机ip和端口号加入RTP会话管理 System.out.println("session.addr:" + session.addr); mgrs[i].initialize(new RTPSocketAdapter(InetAddress .getByName(session.addr), session.port, session.ttl)); BufferControl bc = (BufferControl) mgrs[i] .getControl("javax.media.control.BufferControl"); if (bc != null) bc.setBufferLength(350); } } catch (Exception e) { e.printStackTrace(); } } /** * Close the players and the session managers. */ protected void close() { // close the RTP session. for (int i = 0; i < mgrs.length; i++) { if (mgrs[i] != null) { mgrs[i].removeTargets("Closing session from AVReceive3"); mgrs[i].dispose(); mgrs[i] = null; } } } /** * SessionListener. */ @SuppressWarnings("deprecation") public synchronized void update(SessionEvent evt) { if (evt instanceof NewParticipantEvent) { Participant p = ((NewParticipantEvent) evt).getParticipant(); System.err.println(" - A new participant had just joined: " + p); } } /** * ReceiveStreamListener */ public synchronized void update(ReceiveStreamEvent evt) { RTPManager mgr = (RTPManager) evt.getSource(); Participant participant = evt.getParticipant(); // could be null. ReceiveStream stream = evt.getReceiveStream(); // could be null. if (evt instanceof RemotePayloadChangeEvent) { System.err.println(" - Received an RTP PayloadChangeEvent."); System.err.println("Sorry, cannot handle payload change."); // System.exit(0); } else if (evt instanceof NewReceiveStreamEvent) { System.out.println("evt instanceof NewReceiveStreamEvent"); try { stream = ((NewReceiveStreamEvent) evt).getReceiveStream(); final DataSource data = stream.getDataSource(); // Find out the formats. RTPControl ctl = (RTPControl) data .getControl("javax.media.rtp.RTPControl"); if (ctl != null) { System.err.println(" - Recevied new RTP stream: " + ctl.getFormat()); } else System.err.println(" - Recevied new RTP stream"); if (participant == null) System.err .println(" The sender of this stream had yet to be identified."); else { System.err.println(" The stream comes from: " + participant.getCNAME()); } // create a player by passing datasource to the Media Manager new Thread() { public void run() { playFrame.remotePlay(data); } }.start(); // Player p = javax.media.Manager.createPlayer(data); // if (p == null) // return; // // p.addControllerListener(this); // p.realize(); // PlayerWindow pw = new PlayerWindow(p, stream); // playerWindows.addElement(pw); // Notify intialize() that a new stream had arrived. synchronized (dataSync) { dataReceived = true; dataSync.notifyAll(); } } catch (Exception e) { System.err.println("NewReceiveStreamEvent exception " + e.getMessage()); return; } } else if (evt instanceof StreamMappedEvent) { System.out.println("evt instanceof StreamMappedEvent"); stream = ((StreamMappedEvent) evt).getReceiveStream(); if (stream != null && stream.getDataSource() != null) { DataSource ds = stream.getDataSource(); // Find out the formats. RTPControl ctl = (RTPControl) ds .getControl("javax.media.rtp.RTPControl"); System.err.println(" - The previously unidentified stream "); if (ctl != null) System.err.println(" " + ctl.getFormat()); System.err.println(" had now been identified as sent by: " + participant.getCNAME()); System.out.println("ds == null" + (ds == null)); } } else if (evt instanceof ByeEvent) { System.err.println(" - Got \"bye\" from: " + participant.getCNAME()); } } /** * ControllerListener for the Players. */ public synchronized void controllerUpdate(ControllerEvent ce) { Player p = (Player) ce.getSourceController(); if (p == null) return; } /** * A utility class to parse the session addresses. */ class SessionLabel { public String addr = null; public int port; public int ttl = 1; SessionLabel(String session) throws IllegalArgumentException { int off; String portStr = null, ttlStr = null; if (session != null && session.length() > 0) { while (session.length() > 1 && session.charAt(0) == '/') { session = session.substring(1); } off = session.indexOf('/'); if (off == -1) { if (!session.equals("")) addr = session; } else { addr = session.substring(0, off); session = session.substring(off + 1); off = session.indexOf('/'); if (off == -1) { if (!session.equals("")) portStr = session; } else { portStr = session.substring(0, off); session = session.substring(off + 1); off = session.indexOf('/'); if (off == -1) { if (!session.equals("")) ttlStr = session; } else { ttlStr = session.substring(0, off); } } } } if (addr == null) throw new IllegalArgumentException(); if (portStr != null) { try { Integer integer = Integer.valueOf(portStr); if (integer != null) port = integer.intValue(); } catch (Throwable t) { throw new IllegalArgumentException(); } } else throw new IllegalArgumentException(); if (ttlStr != null) { try { Integer integer = Integer.valueOf(ttlStr); if (integer != null) ttl = integer.intValue(); } catch (Throwable t) { throw new IllegalArgumentException(); } } } } public static void main(String argv[]) { String[] strs = { "125.221.165.126/9994", "125.221.165.126/9996" }; MediaReceive avReceive = new MediaReceive(strs); avReceive.initialize(); } }
package vidioPlay; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Component; import java.awt.Dimension; import java.awt.FlowLayout; import java.awt.Graphics; import java.awt.Rectangle; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.IOException; import javax.media.ControllerEvent; import javax.media.ControllerListener; import javax.media.DataSink; import javax.media.NoPlayerException; import javax.media.Player; import javax.media.Processor; import javax.media.protocol.DataSource; import javax.swing.ImageIcon; import javax.swing.JButton; import javax.swing.JPanel; public class PlayPane extends JPanel { private ImageIcon videoReqIcon = new ImageIcon("videoReq.jpg"); private ImageIcon VideolocalIcon = new ImageIcon("localVideo.jpg"); private boolean isViewBigPlaying = false; private boolean isViewSmallPlaying = false; private JPanel viewBigPane; private JPanel viewSmallPane; private JPanel controlPane; private JButton closeButton; private boolean localPlay = false; private boolean remotePlay = false; private DataSource localData; private DataSource remoteData; private boolean isViewRun = true; private boolean isShow = true; // private Player localPlayer = null; private Player remotePlayer = null; // private Processor videotapeProcessor = null; private Player videotapePlayer = null; private DataSink videotapeFileWriter; public PlayPane() { this.setLayout(new BorderLayout()); // 视图面板 viewBigPane = new JPanel() { public void paintComponent(Graphics g) { super.paintComponent(g); if (!isViewBigPlaying) { g.drawImage(videoReqIcon.getImage(), 1, 1, videoReqIcon.getIconWidth(), videoReqIcon.getIconHeight(), null); g.drawRect(getSmallPlayRec().x - 1, getSmallPlayRec().y - 1, getSmallPlayRec().width + 1, getSmallPlayRec().height + 1); } else { } } }; viewBigPane.setBackground(Color.black); this.add(viewBigPane, BorderLayout.CENTER); viewBigPane.setLayout(null); // /////////////////////////////// viewSmallPane = new JPanel() { public void paintComponent(Graphics g) { super.paintComponent(g); if (!isViewSmallPlaying) { g.drawImage(VideolocalIcon.getImage(), 0, 0, null); } else { } } }; viewSmallPane.setBounds(getSmallPlayRec()); viewBigPane.add(viewSmallPane); viewSmallPane.setLayout(null); // 控制面板组件 closeButton = new JButton("挂断"); controlPane = new JPanel(); controlPane.setLayout(new FlowLayout(FlowLayout.RIGHT, 0, 0)); controlPane.add(closeButton); this.add(controlPane, BorderLayout.SOUTH); closeButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { if (localPlayer != null) { localPlayer.stop(); } if (remotePlayer != null) { remotePlayer.stop(); } if (videotapePlayer != null) { videotapePlayer.stop(); } if (videotapeProcessor != null) { videotapeProcessor.stop(); } if (videotapeFileWriter != null) { try { videotapeFileWriter.stop(); videotapeFileWriter.close(); } catch (IOException e1) { } } } }); // this.setMinimumSize(new Dimension(videoReqIcon.getIconWidth()+2, // 241)); // this.setPreferredSize(new Dimension(videoReqIcon.getIconWidth()+2, // 241)); } public Dimension getMinimumSize() { System.out .println("controlPane.getHeight():" + controlPane.getHeight()); return new Dimension(videoReqIcon.getIconWidth() + 2, videoReqIcon.getIconHeight() + controlPane.getHeight()); } public Dimension getPreferredSize() { System.out .println("controlPane.getHeight():" + controlPane.getHeight()); return new Dimension(videoReqIcon.getIconWidth() + 2, videoReqIcon.getIconHeight() + controlPane.getPreferredSize().height); } public void localPlay(DataSource dataSource) { this.setLocalData(dataSource); try { localPlayer = javax.media.Manager.createPlayer(dataSource); localPlayer.addControllerListener(new ControllerListener() { public void controllerUpdate(ControllerEvent e) { if (e instanceof javax.media.RealizeCompleteEvent) { Component comp = null; comp = localPlayer.getVisualComponent(); if (comp != null) { // 将可视容器加到窗体上 comp.setBounds(0, 0, VideolocalIcon.getIconWidth(), VideolocalIcon.getIconHeight()); viewSmallPane.add(comp); } viewBigPane.validate(); } } }); localPlayer.start(); localPlay = true; } catch (NoPlayerException e1) { e1.printStackTrace(); } catch (IOException e1) { e1.printStackTrace(); } } private Rectangle getSmallPlayRec() { int bigShowWidth = videoReqIcon.getIconWidth(); int bigShowHeight = videoReqIcon.getIconHeight(); int smallShowWidth = VideolocalIcon.getIconWidth(); int smallShowHeight = VideolocalIcon.getIconHeight(); return new Rectangle(bigShowWidth - smallShowWidth - 2, bigShowHeight - smallShowHeight - 2, smallShowWidth, smallShowHeight); } public void remotePlay(DataSource dataSource) { this.setLocalData(dataSource); remotePlay = true; try { remotePlayer = javax.media.Manager.createPlayer(dataSource); remotePlayer.addControllerListener(new ControllerListener() { public void controllerUpdate(ControllerEvent e) { if (e instanceof javax.media.RealizeCompleteEvent) { Component comp; if ((comp = remotePlayer.getVisualComponent()) != null) { // 将可视容器加到窗体上 comp.setBounds(1, 1, videoReqIcon.getIconWidth(), videoReqIcon.getIconHeight()); viewBigPane.add(comp); } viewBigPane.validate(); } } }); remotePlayer.start(); remotePlay = true; } catch (NoPlayerException e1) { e1.printStackTrace(); } catch (IOException e1) { e1.printStackTrace(); } } public void closeViewUI() { isShow = false; } public boolean isViewRunning() { return isViewRun; } public boolean isShowing() { return isShow; } public void localReady() { localPlay = true; } public void remoteReady() { remotePlay = true; } public boolean isRemotePlay() { return remotePlay; } public void setRemotePlay(boolean remotePlay) { this.remotePlay = remotePlay; } public DataSource getRemoteData() { return remoteData; } public void setRemoteData(DataSource remoteData) { this.remoteData = remoteData; } public boolean isLocalPlay() { return localPlay; } public void setLocalPlay(boolean localPlay) { this.localPlay = localPlay; } public DataSource getLocalData() { return localData; } public void setLocalData(DataSource localData) { this.localData = localData; } }
之后操作如下: 1、安装JMF软件(如果不想安装,就请阅读我转载一篇文章): 2、新建工程将源代码加入工程src下 3、导入第三方包jmf.jar,和fmj.jar(如果网上找不到,请加入该群83945749) 4、运行的时候先运行MediaTransmit类,后运行MediaReceive类 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。
推荐链接
|
|
返回顶楼 | |
发表时间:2011-06-20
很厉害的大学生
|
|
返回顶楼 | |
发表时间:2011-06-21
pywepe 写道 很厉害的大学生
哪里表明身份的? |
|
返回顶楼 | |
发表时间:2011-06-21
偶尔玩玩 写道 pywepe 写道 很厉害的大学生
哪里表明身份的? 这哥们算命的 |
|
返回顶楼 | |
发表时间:2011-06-21
bobotc 写道 偶尔玩玩 写道 pywepe 写道 很厉害的大学生
哪里表明身份的? 这哥们算命的 看背后那门很像寝室的门吧。。。 |
|
返回顶楼 | |
发表时间:2011-06-21
V5啊。哥们
|
|
返回顶楼 | |
发表时间:2011-06-21
java在这么方面 还真的做的不行 如果真的要java来做im 音视频都会用flash来通信
|
|
返回顶楼 | |
发表时间:2011-06-21
试试在 Windows 7里跑跑看, 貌似JMF只能在XP里能跑起来
|
|
返回顶楼 | |
发表时间:2011-06-22
挺不错的!
|
|
返回顶楼 | |
发表时间:2011-06-24
好长时间没搞JMF了;不知道发展咋样了
jmf的源码是不公开的,当年为了找源码,花了三天的业余时间才找到 |
|
返回顶楼 | |