基于JMF的视频传输源码(亲试可行)

转载 2012年03月24日 09:21:59

接收端

import java.applet.Applet;
import java.awt.Button;
import java.awt.Component;
import java.awt.TextField;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.net.InetAddress;

import javax.media.ControllerEvent;
import javax.media.ControllerListener;
import javax.media.Player;
import javax.media.RealizeCompleteEvent;
import javax.media.control.BufferControl;
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.SessionAddress;
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.SessionEvent;
import javax.media.rtp.event.StreamMappedEvent;

// 头一段时间做jmf程序,要把截获的视频流,潜入到网页中,并播放;
// 在网上找了很多例子,是弹出 frame 框形式的,没有做成嵌入到 applet 里面,
// 自己做了一个嵌入到 applet 里面的。也就是把播放器嵌入到 applet 里面,
// 而不是弹出一个 Frame 框,并且定义了两个按钮;一个启动,一个停止;以下是源码; 
// 解压码是:xuedijiujiu 这是个客户端 applet 程序程序,实现视频的接受和播放;
// 服务器端可以用JMStudio来辅助调试; 程序仅供研究
public class AudioVideoReceiver extends Applet implements ControllerListener,
		ReceiveStreamListener, SessionListener, Runnable {
	private static final long serialVersionUID = -5570418604643606114L;
	
	RTPManager rtpManager = null;
	Player player = null;
	boolean dataReceived = false; // 是否接收到数据的标志
	Object dataSync = new Object();	// 专门用于锁的对象

	Component component1;
	Component component2;
	
	Button btnStartVideo;
	Button btnStopVideo;

	TextField tfServerIP;

	public void init() {
		this.setLayout(null);
		// 添加两个按钮,并注册相应的事件!
		btnStartVideo = new Button("start");
		btnStartVideo.setBounds(10, 350, 80, 20);
		btnStartVideo.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				if (player == null) {
					Thread t = new Thread(AudioVideoReceiver.this);
					t.start();
					System.out.println("thread start");
				}
			}
		});
		this.add(btnStartVideo);
		// -----------------------------------------------------
		btnStopVideo = new Button("stop");
		btnStopVideo.setBounds(150, 350, 80, 20);
		btnStopVideo.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				if (player != null) {
					remove(component1);
					remove(component2);
				}
				stopVideo();
				System.out.println("thread stop");
			}
		});
		this.add(btnStopVideo);
		this.setSize(350, 380);
	}

	protected boolean initialize() {
		try {
			rtpManager = RTPManager.newInstance();	// 为每一个RTP会话产生一个RTP管理器
			rtpManager.addSessionListener(this);	// 添加会话监听
			rtpManager.addReceiveStreamListener(this); // 添加接收到数据的监听
			
			// 特别注意的地方:不能将本地地址设置为“127.0.0.1”,必须要实际的ip地址!!
//			InetAddress ipAddress = InetAddress.getByName("224.224.123.123");	// 接收广播
			InetAddress ipAddress = InetAddress.getByName("172.17.21.170");	// 接收单播

			SessionAddress localAddress = null;
			SessionAddress targetAddress = null;
			// 说明:当 ip 地址为“224.224.123.123”的时候,属于组播(亦称广播,能被多个接收端接收)
			// 		当 ip 地址为“110.52.206.144”的时候,属于单播,会自动跳到 else 分支中去!!
			if (ipAddress.isMulticastAddress()) { // 对于组播,本地和目的地的IP地址相同,均采用组播地址
System.out.println("is MulticastAddress()");
				localAddress = new SessionAddress(ipAddress, 22222);
				targetAddress = new SessionAddress(ipAddress, 22222);
			} else {
System.out.println("is Not MulticastAddress()");				
				localAddress = new SessionAddress(InetAddress.getLocalHost(), 8888); // 用本机IP地址和端口号构造源会话地址
				targetAddress = new SessionAddress(ipAddress, 3000); // 用目的机(发送端)的IP地址和端口号构造目的会话地址
			}

			rtpManager.initialize(localAddress); // 将本机会话地址给RTP管理器
			BufferControl bc = (BufferControl)rtpManager.getControl("javax.media.control.BufferControl");
			if (bc != null) {
				bc.setBufferLength(350); // 设置缓冲区大小(也可以使用其他值)
			}

			rtpManager.addTarget(targetAddress); // 加入目的会话地址
		} catch (Exception e) {
			System.err.println("Cannot create the RTP Session: " + e.getMessage());
			return false;
		}

		// 等待数据到达
		long then = System.currentTimeMillis();
		long waitingPeriod = 6000; // 最多等待60秒
		try {
			synchronized (dataSync) {
				// 等待上面所设定的时间
				while (!dataReceived && System.currentTimeMillis() - then < waitingPeriod) { 
					if (!dataReceived) {
						System.err.println("  - Waiting for RTP data to arrive...");
					}
					dataSync.wait(1000);
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
		}

		if (!dataReceived) { // 在设定的时间内没有等到数据
			System.err.println("No RTP data was received.");
			player.close();
			player.stop();
			return false;
		}
		return true;
	}

	public void start() {
		if (player != null) {
			player.start();
		}
	}

	public void stopVideo() {
		if (player != null) {
			player.close();
			player = null;
		}
		if (rtpManager != null) {
			rtpManager.removeTargets("Closing session from RTPReceive");
			rtpManager.dispose(); // 关闭RTP会话管理器
			rtpManager = null;
		}
	}

	public void destroy() {
		if (player != null) {
			player.close();
			player = null;
		}
		if (rtpManager != null) {
			rtpManager.removeTargets("Closing session from RTPReceive");
			rtpManager.dispose(); // 关闭RTP会话管理器
			rtpManager = null;
		}
	}

	public synchronized void controllerUpdate(ControllerEvent event) {
		if (event instanceof RealizeCompleteEvent) {
			if ((component1 = player.getVisualComponent()) != null) {
				this.add(component1);
			}
			component1.setBounds(10, 10, 300, 300);
			if ((component2 = player.getControlPanelComponent()) != null) {
				this.add(component2);
			}
			component2.setBounds(10, 310, 300, 20);
			validate();
			System.out.println("palyer");
		}
	}

	public synchronized void update(ReceiveStreamEvent evt) {
		@SuppressWarnings("unused")
		RTPManager mgr = (RTPManager) evt.getSource();
		Participant participant = evt.getParticipant(); // 得到加入者(发送者)
		ReceiveStream stream = evt.getReceiveStream(); // 得到接收到的数据流

		if (evt instanceof NewReceiveStreamEvent) { // 接收到新的数据流
			try {
				stream = ((NewReceiveStreamEvent) evt).getReceiveStream(); // 得到新数据流
				DataSource ds = stream.getDataSource(); // 得到数据源

				RTPControl ctl = (RTPControl) ds
						.getControl("javax.media.rtp.RTPControl"); // 得到RTP控制器
				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());
				}
				player = javax.media.Manager.createPlayer(ds); // 通过数据源构造一个媒体播放器
				if (player == null) {
					return;
				}
				player.addControllerListener(this); // 给播放器添加控制器监听
				player.realize();
				synchronized (dataSync) {
					dataReceived = true;
					dataSync.notifyAll();
				}
			} catch (Exception e) {
				System.err.println("NewReceiveStreamEvent exception" + e.getMessage());
				return;
			}
		} else if (evt instanceof StreamMappedEvent) { // 数据流映射事件
			if (stream != null && stream.getDataSource() != null) {
				DataSource ds = stream.getDataSource();
				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());
			}
		} else if (evt instanceof ByeEvent) { // 数据接收完毕
			System.err.println("  - Got /'bye/' from: "	+ participant.getCNAME());

			if (player != null) {
				player.close(); // 关闭播放窗口
			}
		}
	}

	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.getCNAME());
		}
	}

	public void run() {
		this.initialize();
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		player.start();
	}
}

发送端

import javax.media.rtp.*;
import javax.media.*;
import javax.media.protocol.*;
import javax.media.control.FormatControl;
import javax.media.control.TrackControl;
import javax.media.format.*;

import java.io.IOException;
import java.net.InetAddress;
import java.util.*;

// 很漂亮的一个音视频信号发送端的程序!接收端可用JMStudio来代替(Open RTP Session选项)!
public class AudioVideoSender {
	private CaptureDeviceInfo captureVideoDevice = null;
	private CaptureDeviceInfo captureAudioDevice = null;
	
	private DataSource videoDataSource = null;
	private DataSource audioDataSource = null;
	private Processor videoProcessor = null;
	private Processor audioProcessor = null;
	
	private RTPManager videortpManager = null;
	private RTPManager audiortpManager = null;
	
	private SendStream videoRtpstream = null;
	private SendStream audioRtpstream = null;
	
	private SessionAddress remoteAddress1 = null;
	private SessionAddress remoteAddress2 = null;
	
	@SuppressWarnings({ "unused", "unchecked" })
	private Vector sendplayerlist = new Vector();
	@SuppressWarnings("unused")
	private boolean terminatedbyClose_sender = false;
	
	// 以下两个在配置数据源的时候要用到!
	private DataSource ds = null;
	private TrackControl[] track = null;

	// 该方法用于获取 "视频" 和 "音频" 捕获设备的 CaptureDeviceInfo 信息对象!
	private void getDeviceInfo() {
		// ①获取视频捕获设备的 CaptureDeviceInfo 对象!
		Vector<?> deviceList = null;
		deviceList = CaptureDeviceManager.getDeviceList(null);
		for (int i = 0; i < deviceList.size(); i++) {
			captureVideoDevice = (CaptureDeviceInfo) deviceList.elementAt(i);
			String name = captureVideoDevice.getName();
			if (name.startsWith("vfw:")) {
				break;
			}
		}
		// ②获取音频捕获设备的 CaptureDeviceInfo 对象!
		deviceList = null;	// 清空 deviceList
		deviceList = CaptureDeviceManager.getDeviceList(new AudioFormat("linear", 8000, 8, 1));
		if (deviceList.size() > 0) {
			captureAudioDevice = (CaptureDeviceInfo) deviceList.elementAt(0);
		} else {
			// 如果找不到支持linear, 8000 Hz, 8 bit, stereo audio这些条件的音频设备的话就退出!
			System.err.println("Device initializing failure!");
			System.exit(-1);
		}
	}
	
	// 为视频捕获设备创建一个视频处理器,如果不能创建的话就退出	
	private void setVideoDataSource() throws Exception {
		ds = Manager.createDataSource(captureVideoDevice.getLocator());
		videoProcessor = Manager.createProcessor(ds);
		videoProcessor.configure();
		// 阻塞在这里直到配置完成
		while (true) {
			if (videoProcessor.getState() == Processor.Configured) {
				break;
			}
		}
		videoProcessor.setContentDescriptor(new ContentDescriptor(ContentDescriptor.RAW));
		track = videoProcessor.getTrackControls();	//getTrackControls() 方法的返回值类型: TrackControl[]
		boolean encodingOk = false;

		// 将 track 设置成我们想要的 track,不想要的 track 统统 disable 掉!
		for (int i = 0; i < track.length; i++) {
			if (!encodingOk && track[i] instanceof FormatControl) {
				if (((FormatControl) track[i]).setFormat(new VideoFormat(
						VideoFormat.JPEG_RTP)) == null) {
					track[i].setEnabled(false);	// 将不可用的(除VideoFormat.JPEG_RTP以外的)磁道全部设为不可用!
				} else {
					encodingOk = true;
				}
			} else {
				// 我们不能将磁道设置为 .GSM 格式,因此 disable 它!
				track[i].setEnabled(false);
			}
		}

		videoProcessor.realize();
		// 阻塞直到完成视频处理器的实现!
		while (true) {
			if (videoProcessor.getState() == Processor.Realized) {
				break;
			}
		}

		try {
			videoDataSource = videoProcessor.getDataOutput();
		} catch (NotRealizedError e) {
			System.exit(-1);	// 不能成功配置视频数据源的话下面肯定进行不下去,直接退出
		}
System.out.println("videoDataSource is ready!");
	}

	// 为音频捕获设备创建一个音频处理器,如果不能创建就退出程序
	private void setAudioDataSource() throws Exception {
		ds = Manager.createDataSource(captureAudioDevice.getLocator());
		audioProcessor = Manager.createProcessor(ds);

		audioProcessor.configure();
		// 阻塞直到完成音频处理器的配置!
		while (true) {
			if (audioProcessor.getState() == Processor.Configured) {
				break;
			}
		}

		audioProcessor.setContentDescriptor(new ContentDescriptor(ContentDescriptor.RAW));
		track = audioProcessor.getTrackControls();
		boolean encodingOk = false;

		for (int i = 0; i < track.length; i++) {
			if (!encodingOk && (track[i] instanceof FormatControl)) {
				if (((FormatControl) track[i]).setFormat(new AudioFormat(
						AudioFormat.ULAW_RTP, 8000, 8, 1)) == null) {
					track[i].setEnabled(false);
				} else {
					encodingOk = true;
				}
			} else {
				track[i].setEnabled(false);
			}
		}
		audioProcessor.realize();
		while (true) {
			if (audioProcessor.getState() == Processor.Realized) {
				break;
			}
		}
		try {
			audioDataSource = audioProcessor.getDataOutput();
		} catch (NotRealizedError e) {
			System.exit(-1);
		}
System.out.println("audioDataSource is ready!");
	}

	// 开始传送视频和音频数据!
	public void transmitStart(String targetAddress, String targetPort) {
		videortpManager = RTPManager.newInstance();
		audiortpManager = RTPManager.newInstance();
		SessionAddress localAddress1 = null;
		SessionAddress localAddress2 = null;

		// create the local endpoint for the local interface on any local port
		localAddress1 = new SessionAddress();
		localAddress2 = new SessionAddress();

		try {
			videortpManager.initialize(localAddress1);
			audiortpManager.initialize(localAddress2);
		} catch (Exception e) {
			e.printStackTrace();
		}

		// specify the remote endpoint of this unicast session
		try {
			InetAddress ipAddress = InetAddress.getByName(targetAddress);
			remoteAddress1 = new SessionAddress(ipAddress, Integer.parseInt(targetPort));
			videortpManager.addTarget(remoteAddress1);
			remoteAddress2 = new SessionAddress(ipAddress, Integer.parseInt(targetPort) + 2);
			audiortpManager.addTarget(remoteAddress2);

			System.out.println();
			System.out.println("data address     " + localAddress1.getDataAddress());
			System.out.println("contorl address  " + localAddress1.getControlAddress());
			System.out.println("data port        " + localAddress1.getDataPort());
			System.out.println("control port     " + localAddress1.getControlPort());
			System.out.println();
			System.out.println("data address     " + localAddress2.getDataAddress());
			System.out.println("contorl address  " + localAddress2.getControlAddress());
			System.out.println("data port        " + localAddress2.getDataPort());
			System.out.println("control port     " + localAddress2.getControlPort());
			System.out.println();
			
			videoRtpstream = videortpManager.createSendStream(videoDataSource, 0);
			audioRtpstream = audiortpManager.createSendStream(audioDataSource, 0);
			videoProcessor.start();
			audioProcessor.start();
			videoRtpstream.start();
			audioRtpstream.start();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	// 停止传送视频和音频数据!
	public void transmitStop() throws IOException, InvalidSessionAddressException {
		videoRtpstream.stop();
		videoRtpstream.close();
		videoProcessor.stop();
		audioRtpstream.stop();
		audioRtpstream.close();
		audioProcessor.close();

		// close the connection if no longer needed.
		videortpManager.removeTarget(remoteAddress1, "client disconnected.");
		audiortpManager.removeTarget(remoteAddress2, "client disconnected.");

		// call dispose at the end of the life-cycle of this RTPManager so
		// it is prepared to be garbage-collected.
		videortpManager.dispose();
		audiortpManager.dispose();

		System.out.println("Now releasing the resource!");
		videoProcessor.deallocate();
		audioProcessor.deallocate();
		videoDataSource.disconnect();
		audioDataSource.disconnect();
	}

	public AudioVideoSender() throws Exception {
		// 初始化音视频捕获设备并为传输做好准备
		this.getDeviceInfo();
		this.setVideoDataSource();
		this.setAudioDataSource();
	}
	
	public static void main(String[] args) {
		try {
			AudioVideoSender tc = new AudioVideoSender();
			tc.transmitStart("172.17.21.152", "8888");
		} catch(Exception e) {
			e.printStackTrace();
		}
	}
}


相关文章推荐

用JAVA中的JMF来打开摄像头

今天学习用JAVA中的JMF来打开摄像头,这个小程序对我来说还是有点难度的。然后我在网上找的一些资料,网上的资料很多而且比较杂,对于我们新人来说,没有分辨能力,走了很多弯路。下面我把我的学习过程展示出...

JMF下载和安装

JMF,全名为Java Media Framework,它可以在java applet和application中使用音频,视频或者其他基于时间的多媒体。JMF所支持的多媒体格式如下:AIFF(.aif...
  • oyzl68
  • oyzl68
  • 2011年11月11日 09:08
  • 31977

基于JMF下的Java的MP3播放

Java的基本API类中是不能播放MP3格式音频。 可以通过JMF(Java Media Framework Java媒体框架)来增加对mp3及更多格式音视频的播放。 JMF是也不可以播放MP3的...
  • Mr_Pang
  • Mr_Pang
  • 2015年08月19日 20:30
  • 2616

Java JMF的使用

望有这方面经验的人交流一下心得 一、jmf 开发环境的设置 下载jmf windows performace pack(我已经下载了,可直接下我提供的包) http://java.sun...

Delphi7高级应用开发随书源码

  • 2003年04月30日 00:00
  • 676KB
  • 下载

java JMF

       Java媒体框架(JMF)使你能够编写出功能强大的多媒体程序,却不用关心底层复杂的实现细节。JMF API的使用相对比较简单,但是能够满足几乎所有多媒体编程的需求。在这篇文章中,我将向你...

Delphi7高级应用开发随书源码

  • 2003年04月30日 00:00
  • 676KB
  • 下载

Delphi7高级应用开发随书源码

  • 2003年04月30日 00:00
  • 676KB
  • 下载

Java基于JMF、FMJ实现打开摄像头实时录制

纪念平时一天的研究成果,本人学了那么多技术从未写过文章,今儿想贡献一下,望大家笑纳,多多指教 1、基于FMJ实现      FMJ是一个Java开源项目它是JMF(Java Media Framewo...

Delphi7高级应用开发随书源码

  • 2003年04月30日 00:00
  • 676KB
  • 下载
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:基于JMF的视频传输源码(亲试可行)
举报原因:
原因补充:

(最多只允许输入30个字)