Java Media Framework(JMF)是一种令人兴奋的通用API,它允许Java开发人员以许多不同的方式处理媒体。 本教程主要通过使用工作示例概述了JMF的一些主要功能。 完成本教程后,您将了解JMF体系结构的主要参与者。 您还将直接使用JMF,使用实时示例和可扩展用于更特定目的的源代码。
本教程涵盖的主题如下:
通过JMF几乎可以进行任何类型的媒体操纵或处理。 JMF必须提供的所有功能的全面讨论已经超出了本教程的范围。 相反,我们将使用三个简化的媒体应用程序来了解框架的构建块。 这样,本教程将为您准备进一步的研究和更具体的应用程序的实现。
本教程将引导您了解使用JMF的基础知识。 为此,我们将创建三个单独的工作示例应用程序。 这些示例中的每一个都将基于前面的示例,显示JMF功能的不同方面。
本教程中的示例假定您已经使用并且已经熟悉Java编程语言。 除了核心的Java和JMF类之外,我们还将使用一些Java AWT和Swing类(用于显示GUI)以及一些Java网络类(用于通过网络传输媒体)。 熟悉GUI和网络类将帮助您更快速地了解此处的讨论和示例,但这不是本教程的前提条件。
我们将使用的示例应用程序如下:
作为第三个练习的一部分,我们将修改启用GUI的媒体播放器,以使其能够接收和播放广播媒体。
请参阅相关主题的文章,教程和其他参考资料,这将有助于您了解更多有关本教程中涉及的主题列表。
要运行本教程中的示例,您需要以下工具和组件:
最终的演示应用程序展示了如何在网络上使用JMF。 如有必要,可以使用机器作为发送器和接收器,在一台机器上运行演示。 但是,要查看通过网络使用JMF的全部功能,您将需要至少两台在同一网络上可访问的计算机。
请参阅相关主题在本教程中完成必要的下载Java 2平台,完整的源文件和其他工具。
在计算机上安装JMF的第一步是通过JMF主页下载安装程序,该页面还包含指向JMF源代码和API文档的链接。 请参阅相关信息的链接,下载JMF。
当前,JMF具有适用于Windows,Solaris,Linux的版本以及可在任何具有JVM的计算机上运行的纯Java版本。 为了提高性能,您应该下载特定于您的操作系统的版本。 使用特定于操作系统的JMF版本编写和编译的任何代码都可以跨其他操作系统移植。 例如,如果下载Solaris版本的JMF,并编译许多类,则可以在Linux计算机上使用这些相同的类而不会出现任何问题。
或者,您可以选择下载JMF的纯Java版本或“跨平台”版本。 此版本不使用任何特定于操作系统的库。 如果您的操作系统没有安装自己的JMF版本,或者您不知道目标计算机将在哪个操作系统上运行,那么跨平台版本是一个不错的选择。
将JMF安装程序下载到桌面后,双击安装程序图标。
大多数安装程序都可以选择在系统目录中安装本机库。 例如,Windows安装程序将显示“将DLL移至Windows / System目录”的选项。 为了获得最佳结果,请选择此选项,因为它可以确保正确安装特定于操作系统的库。
您还应该选择在安装过程中更新系统CLASSPATH
和PATH
变量的选项。 如果关闭此选项,请记住在编译或运行本教程中的任何示例时将JMF jar文件包含在类路径中。
在本节中,我们将逐步完成创建简单音频播放器的第一个练习。 本示例向您介绍Manager
类和Player
接口,它们是构建大多数基于JMF的应用程序的两个主要部分。
此示例的功能目标是通过命令行界面播放本地音频文件。 我们将遍历源代码并回顾每一行中发生的情况。 完成本节后,您将拥有一个演示应用程序,可以在其上播放JMF支持的任何音频文件类型,包括MP3,WAV和AU等。
请参考源代码分发中的SimpleAudioPlayer.java文件以执行此练习。
SimpleAudioPlayer
类的前几行包括以下调用,这些调用导入所有必需的类:
import javax.media.*;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.net.MalformedURLException;
javax.media
软件包是JMF定义的众多软件包之一。 javax.media
是核心软件包,其中包含Manager
类和Player
接口的定义。 在本节中,我们将重点介绍Manager
类和Player
接口,并在后面的部分中处理其他一些javax.media
类。
除了import javax.media
语句之外,上述代码片段还包含几个import语句,这些语句创建了我们的媒体播放器的输入。
在下一个代码片段中,定义了公共类SimpleAudioPlayer
和Player
实例变量:
public class SimpleAudioPlayer {
private Player audioPlayer = null;
Player
一词听起来可能很熟悉,因为它是基于我们对基于音频和视频的媒体播放器的常用用法。 实际上,此接口的实例的行为与实际情况非常相似。 Player
公开与物理媒体播放器(例如,立体声系统或VCR)的功能有关的方法。 例如,JMF媒体Player
具有启动和停止媒体流的功能。 在本节中,我们将使用Player
的启动和停止功能。
JMF使获取给定媒体文件的Player
实例非常简单。 Manager
类充当创建JMF中公开的许多特定接口类型(包括Player
接口)的工厂。 因此, Manager
类负责创建我们的Player
实例,如下所示:
public SimpleAudioPlayer(URL url) throws IOException,
NoPlayerException,
CannotRealizeException {
audioPlayer = Manager.createRealizedPlayer(url);
}
public SimpleAudioPlayer(File file) throws IOException,
NoPlayerException,
CannotRealizeException {
this(file.toURL());
}
如果您关注本节的源代码,您可能已经注意到Manager
类包含用于创建Player
实例的其他方法。 在后面的部分中,我们将探讨其中的一些方法,例如传递DataSource
或MediaLocator
实例。
JMF定义了Player
实例可能处于的许多不同状态。这些状态如下:
因为使用媒体通常会占用大量资源,所以JMF对象公开的许多方法都是非阻塞的,并允许通过一系列事件侦听器异步通知状态更改。 例如, Player
必须先经过“ 预取”和“已实现”状态,然后才能开始。 由于完成这些状态更改可能需要一些时间,因此JMF媒体应用程序可以将一个线程分配给Player
实例的初始创建,然后继续进行其他操作。 当Player
准备就绪时,它将通知应用程序其状态更改。
在像我们这样的简单应用程序中,这种多功能性并不是那么重要。 因此, Manager
类还公开了用于创建Realized播放Manager
实用程序方法。 调用createRealizedPlayer()
方法会使调用线程阻塞,直到播放器达到Realized状态为止。 要调用非阻塞播放器创建方法,我们在Manager
类上使用createPlayer()
方法之一。 以下代码行创建了一个Realized播放器,我们在示例应用程序中需要它:
audioPlayer = Manager.createRealizedPlayer(url);
设置要启动或停止的Player
实例就像在Player
上调用易于识别的方法一样简单,如下所示:
public void play() {
audioPlayer.start();
}
public void stop() {
audioPlayer.stop();
audioPlayer.close();
}
在SimpleAudioPlayer
类上调用play()
方法仅将调用委派给Player
实例上的start()
方法。 调用此方法后,您应该听到通过本地扬声器播放的音频文件。 同样, stop()
方法委派给播放器以停止和关闭Player
实例。
关闭Player
实例将释放用于读取或播放媒体文件的所有资源。 因为这是一个简单的示例,所以关闭Player
是结束会话的一种可接受的方式。 但是,在实际应用程序中,您应该在关闭Player
之前仔细考虑是否要摆脱Player
。 关闭播放器后,必须先创建一个新的Player
实例(并等待其进行所有状态更改),然后才能再次播放媒体。
最后,此媒体播放器应用程序包含main()
方法,该方法允许通过传入文件名从命令行调用它。 在main()
方法中,我们进行以下调用,该调用将创建SimpleAudioPlayer
:
File audioFile = new File(args[0]);
SimpleAudioPlayer player = new SimpleAudioPlayer(audioFile);
播放音频文件之前,我们要做的唯一一件事就是在创建的音频播放器上调用play()
方法,如下所示:
player.play();
要停止并清理音频播放器,我们进行以下调用,该调用也位于main()
方法中:
player.stop();
通过在命令提示符下键入javac SimpleAudioPlayer.java
来编译示例应用程序。 这将在工作目录中创建SimpleAudioPlayer.class
文件。
然后通过在命令提示符处键入以下内容来运行示例应用程序:
java SimpleAudioPlayer audioFile
用本地系统上音频文件的文件名替换audioFile 。 任何相对文件名都将相对于当前工作目录进行解析。 您应该看到一些消息,指示正在播放的文件。 要停止播放,请按Enter键。
如果编译失败,请检查以确保JMF jar文件包含在当前CLASSPATH
环境变量中。
在上一节中,我们逐步完成了设置应用程序的步骤,该应用程序使您可以通过命令行界面播放音频文件。 JMF的一大功能是,您无需了解媒体文件类型就可以配置媒体播放器。 一切都在内部处理。 例如,在我们之前的示例中,由于MP3设置是为我们处理的,因此我们无需告诉应用程序专门为MP3文件创建Player
。
正如您将在本节中看到的那样,处理视频文件也是如此。 JMF处理与媒体文件类型接口的所有详细信息。
处理视频媒体而不是音频的主要区别在于,我们必须创建屏幕的可视表示形式才能显示视频。 幸运的是,JMF为我们处理了许多这些细节。 我们将像在上一个示例中一样创建一个Player
实例,并获取许多视觉组件以直接从JMF对象创建视觉媒体查看器。
在本节中,我们将逐步介绍第二个示例应用程序。 请参考源代码分发中的MediaPlayerFrame.java来遵循此练习。
在本节中,我们将创建一个可以显示和运行本地计算机上的音频和视频媒体的应用程序。 作为此练习的一部分,我们将探索JMF的一些内置GUI组件。 熟悉AWT和Swing将帮助您遵循示例,但不是必需的。 除非它与JMF GUI组件直接相关,否则我们将不对GUI的源代码做更多的详细介绍。 您会发现源代码注释中解释了许多此处未涵盖的细节。
我们将在本示例中使用的大多数概念,类和方法将从第一个示例开始熟悉。 设置Player
的基本知识几乎相同。 最大的不同是,我们将对Player
实例进行更深入的研究,尤其是在从Player
获取有关媒体的信息时。
视频播放器示例旨在从命令行运行,就像音频播放器示例一样,但是此示例基于GUI。 与上一节一样,我们首先调用应用程序并传入媒体的文件名。 接下来,该应用程序将显示一个窗口,其中包含可让我们操纵媒体的组件。
在MediaPlayerFrame
的开头MediaPlayerFrame
行中,我们定义了该类并扩展了javax.swing.JFrame
类。 这就是我们将媒体播放器定义为桌面上单独的窗口的方式。 然后,任何创建媒体播放器类实例的客户端都可以使用JFrame
类上定义的show()
方法来显示它。
下面的示例屏幕截图显示了播放MPEG电影的MediaPlayerFrame
:
Player
界面公开了获取对选定视觉组件的引用的方法。 在MediaPlayerFrame
我们使用以下组件:
player.getVisualComponent()
是负责显示任何视频媒体的可视组件。 player.getControlPanelComponent()
是用于处理基于时间的操作(即开始,停止,倒带)以及在媒体流上包含一些有用信息的可视组件。 player.getGainControl().getControlComponent()
是用于处理音量(增益)操作的可视组件。 getGainControl()
方法返回一个GainControl
实例,该实例可用于以编程方式更改增益级别。 上面所有的接口方法都返回java.awt.Component
类的实例。 每个实例都是一个视觉组件,可以添加到我们的框架中。 这些组件直接与Player
绑定,因此对这些组件上的视觉元素的任何操纵都会引起Player
显示的媒体的相应变化。
重要的是,在将它们添加到框架之前,请确保这些组件中的每个都不为null
。 因为并非每种类型的媒体播放器都包含每种类型的视觉组件,所以我们只应添加与我们拥有的播放器类型相关的组件。 例如,音频播放器通常没有视觉组件,因此getVisualComponent()
返回null
。 您不想将视觉组件添加到音频播放器框架。
Player
实例还可以通过其getControl()
和getControls()
方法公开其他控件getControls()
返回Control
对象的集合,而getControl()
方法查找特定的Control
。 不同类型的播放器可能会选择公开特定于给定媒体类型或用于获取该媒体的传输机制的操作的控件。 如果要编写仅处理某些媒体类型的应用程序,则可以依靠通过Player
实例可用的某些Control
对象。
因为我们的播放器非常抽象,并且设计为可以与许多不同的媒体类型一起使用,所以我们只向用户公开所有Control
对象。 如果发现任何其他控件,则可以使用getControlComponent()
方法将其相应的可视组件添加到选项卡式窗格中。 这样,用户将能够通过播放器查看任何这些组件。 以下代码片段向用户公开了所有控件对象:
Control[] controls = player.getControls();
for (int i = 0; i < controls.length; i++) {
if (controls[i].getControlComponent() != null) {
tabPane.add(controls[i].getControlComponent());
}
}
为了使实际应用程序对Control
实例做一些有用的事情(除了能够显示其可视组件),应用程序需要知道Control
的特定类型并将其转换为该类型。 此后,应用程序可以使用控件以编程方式操纵媒体。 例如,如果您知道所使用的媒体始终公开javax.media.control.QualityControl
类型的Control
,则可以将其QualityControl
转换为QualityControl
接口,然后通过调用QualityControl
接口上的任何方法来更改任何质量设置。
我们新的基于GUI的媒体播放器和第一个简单播放器之间的最后一个大区别是,我们将使用MediaLocator
对象而不是URL
创建Player
实例,如下所示:
public void setMediaLocator(MediaLocator locator) throws IOException,
NoPlayerException, CannotRealizeException {
setPlayer(Manager.createRealizedPlayer(locator));
}
我们将在后面的部分中讨论进行此更改的原因。 现在,将MediaLocator
对象与URL非常相似,因为两者都描述了网络上的资源位置。 实际上,您可以从URL创建MediaLocator
,也可以从MediaLocator
获取URL。 我们的新媒体播放器从URL创建MediaLocator
实例,并使用该实例在文件上创建Player
。
通过在命令提示符下键入javac MediaPlayerFrame.java
来编译示例应用程序。 这将在工作目录中创建一个名为MediaPlayerFrame.class
的文件。
要运行示例应用程序,请在命令提示符下键入以下内容:
java MediaPlayerFrame mediaFile
您应该用本地系统上媒体文件的文件名替换mediaFile (音频或视频都可以)。 任何相对文件名都将相对于当前工作目录进行解析。 您应该看到一个窗口,该窗口显示用于处理媒体文件的GUI控件。 有关可在JMF中使用的音频和视频文件格式的列表,请参阅参考资料 。
如果初始编译失败,请检查以确保JMF jar文件包含在当前的CLASSPATH
环境变量中。
在本节的前面,您看到了播放MPEG视频文件的视频播放器的屏幕截图。 以下屏幕快照显示了播放MP3文件的音频播放器:
查看完整的MediaPlayerFrame源代码,以进一步研究本练习中的示例。
既然您已经看到了使用JMF播放本地媒体文件有多么容易,我们将退后一步,看看如何使用JMF创建更复杂的基于媒体的应用程序的大图。 这绝不是对JMF架构的全面调查。 相反,本节将为您提供有关如何组合高级JMF组件以创建所需效果的一般概念。
JMF组件体系结构非常灵活,其组件通常可以分为三类:
Player
的输出。) 从这个描述中,您可能会认为JMF组件体系结构听起来很像典型的立体声或VCR。 可以想象使用JMF的方式类似于打开电视或调整立体声上的声音。 例如,可以通过以下基于组件的术语来考虑录制喜爱的电视节目的简单操作:
下图说明了JMF数据处理模型,并给出了每种类型的一些示例:
使用此模型,可以很容易地遵循我们的前两个示例,从文件输入到本地计算机上的音频和视频输出。 在以下各节中,我们还将通过在网络上广播和接收音频媒体来探索JMF的某些网络功能。
通过将JMF的输入,流程和输出模型链接在一起,我们可以开始构想通过JMF可以实现的许多基于媒体的操作。 一个示例是将一种类型的媒体转换为另一种类型,并将输出存储到新文件中。 例如,假设我们想将WAV格式的音频文件转换为MP3格式而又不破坏原始文件。 以下流程模型说明了我们执行转换所要采取的步骤:
此示例的输入是WAV文件。 它由媒体格式转换器处理,并将输出放置在新文件中。 现在,让我们通过JMF API来研究此模型中的每个步骤。 我们将使用输入,过程和输出模型作为概念上的指导。
在JMF中,输入通常由MediaLocator
对象表示。 如前所述, MediaLocator
外观和行为非常类似于URL,因为它可以唯一地标识网络中的资源。 实际上,可以使用URL创建MediaLocator
。 我们在之前的两个示例应用程序中做到了这一点。
就我们的媒体转换示例而言,我们可以构建一个MediaLocator
来描述原始的WAV文件。 正如我们将在接下来的几节中看到的那样, MediaLocator
也可以用来表示通过网络广播的媒体流。 在这种情况下, MediaLocator
构建MediaLocator
来标识本地文件系统上的文件, MediaLocator
将描述广播的URL,就像通过URL标识Web上的资源一样。
成功创建的URL
对象要求在系统上安装适当的java.net.URLStreamHandler
类。 流处理程序的目的是能够处理URL描述的流类型。 MediaLocator
对象没有此要求。 例如,我们的下一个应用程序将使用实时传输协议(RTP)通过网络传输音频。 因为大多数系统没有为RTP协议安装URLStreamHandler
,所以为此目的创建URL
对象的尝试将失败。 对于此应用程序,只有MediaLocator
对象可以成功。
有关URL
对象以及创建和注册URLStreamHandler
更多信息,请参考JDK API文档中的javadoc(请参阅参考资料 )。
当我们使用JMF时,应用程序的处理器组件由Processor
接口的实例表示。 您应该已经对Processor
有所了解,因为它是Player
界面的扩展。 由于Processor
从继承Player
界面,它也继承了所有的从有效状态的Player
。 此外, Processor
还添加了两个状态:正在配置和已配置 。 这些额外状态(和关联的事件)用于在Processor
从输入流中收集信息时进行通信。
对于最后的示例应用程序,我们将创建一个Processor
,将以MP3格式编码的音频转换为适合通过网络广播的格式。 我们将在后面的面板中讨论创建简单Processor
的步骤。
有几种方法可以表示JMF流程模型的输出阶段。 最简单的方法(也是我们在最后一个示例中将使用的方法)是javax.media.DataSink
接口。 DataSink
读取媒体内容并将其呈现到某个目标。 在本节开头的音频格式转换方案中,MP3(输出)文件将由DataSink
表示。 在最后一个示例中,我们将使用DataSink
实际完成通过网络广播音频媒体的工作。 甲DataSink
通过创建Manager
类通过指定一个DataSource
(输入到DataSink
)和MediaLocator
(的输出DataSink
)。
DataSource
实例表示输入数据,该数据用于Player
, Processor
和DataSink
。 Processor
的输出也表示为DataSource
对象。 这是Processor
可以链接在一起以对同一媒体数据执行多项操作的方式。 这也是将Processor
的输出用作Player
或DataSink
(将媒体渲染到输出目标)的输入的方式。
DataSink
的最终目标由MediaLocator
对象指定。 和以前一样, MediaLocator
代表网络资源。 也就是说,它将在此处渲染媒体流。
JMF直接内置了许多对网络友好的功能,这使得客户端程序员可以非常轻松地通过网络广播和接收媒体。 当网络上的用户想要接收任何类型的流媒体时,在观看媒体之前,他不必等待整个广播下载到机器上。 用户应该能够实时观看广播。 这个概念称为流媒体 。 通过流媒体,网络客户端可以接收由另一台机器广播的音频,甚至可以截获实时的视频广播。
实时传输协议(RTP)在IETF RFC 1889中进行了定义。RTP的开发是为了以快速,可靠的方式通过网络承载对时间非常敏感的数据,JTP中使用了RTP,为用户提供了一种将媒体流传输到的方法。其他网络节点。
在本节中,我们将逐步介绍最终的示例应用程序。 在这里,您将学习如何将存储在一台计算机上的MP3文件广播到同一网络中的其他计算机。 实际的MP3源文件永远不会离开主机,也不会被复制到任何其他机器上。 而是将其转换为可以使用RTP广播并通过网络发送的格式。 在被客户端接收后,源文件(现在以RTP数据包的形式)可以再次转换为适合在接收机上播放的格式。
请参阅源代码分发中的MediaTransmitter.java文件,以按照本节中的练习进行操作。
我们可以根据上一节中定义的过程模型来讨论最后一个示例。 在发送机上,过程模型如下所示:
实际上, MediaTransmitter
对象的源代码包含以下三行:
private MediaLocator mediaLocator = null;
private DataSink dataSink = null;
private Processor mediaProcessor = null;
这三个实例变量可以直接映射到上面的过程模型图中,如下所示:
mediaProcessor
变量是我们的处理器; 它将负责将我们的音频媒体从MP3文件格式转换为适合通过RTP协议传输的格式。 dataSink
变量是我们的输出块。 DataSink
我们将指定MediaLocator
,它是DataSink
的实际目的地。 当我们通过运行我们的处理媒体DataSink
,它会被发送到我们指定的任何位置(一个或多个) MediaLocator
。
在前面的两个练习中,我们使用从文件中获取的URL创建了MediaLocator
实例。 在本练习中,我们必须创建一个MediaLocator
来描述用于媒体传输的网络输出。 换句话说,我们必须创建一个MediaLocator
,它可以作为音频广播的目标。 RTP MediaLocator
遵循以下形式,看起来像典型的URL:
rtp://address:port/content-type
让我们看一下上述URL规范的每一部分:
192.168.1
的子网中,并且想广播到所有节点,则可以将192.168.1.255
指定为地址; 这将使子网中的每个节点都可以收听广播媒体。 audio
。 以下RTP广播MediaLocator
简单示例将使指定网络上的任何计算机接收流式媒体:
rtp://192.168.1.255:49150/audio
我们在setDataSource()
方法中所做的第一件事是创建一个Processor
实例。 以下Processor
将负责将我们的MP3音频媒体转换为音频的RTP表示形式:
public void setDataSource(DataSource ds) throws IOException,
NoProcessorException, CannotRealizeException, NoDataSinkException {
mediaProcessor = Manager.createRealizedProcessor(
new ProcessorModel(ds, FORMATS, CONTENT_DESCRIPTOR));
在Manager
类中,我们可以通过以下两种方法类型之一创建Processor
对象: createProcessor()
或createRealizedProcessor()
。 您可能会注意到,这两种方法的行为方式与之前练习中用来创建Player
的方法类似。 对于当前示例,我们将创建一个实现的Processor
。 我们之所以能够这样做,是因为我们正在处理的应用程序很简单,并且直到Processor
处于“已实现”状态之后,我们才愿意进行任何实际工作。
To create a realized Processor
, we need to create a ProcessorModel
instance that describes the media types for the inputs and outputs to the Processor
. In order to create the ProcessorModel
, we need the following things:
DataSource
, which is the media that will be processed (the input file). javax.media.Format
array, which describes the format of the input media. javax.media.protocol.ContentDescriptor
instance, which describes the output format of our processor. The DataSource
for the transmitter is passed in as a parameter to the method. Because our MediaTransmitter
class will always be used to take one type of input media (MP3) and create one type of output (audio RTP) these objects are declared as static. We create a new javax.media.format.AudioFormat
instance to describe the media input type (see the javadoc to learn about the available formats). This is why our processor may only take MP3 audio files.
We also create a javax.media.protocol.ContentDescriptor
instance to describe what we want the output of our processor to be. In our case this is an RTP media stream. This is why our processor may only produce RTP streams.
The following code snippet shows how we set up the format and content descriptor variables, which are used to create the ProcessorModel
object.
private static final Format[] FORMATS = new Format[] {
new AudioFormat(AudioFormat.MPEG_RTP)};
private static final ContentDescriptor CONTENT_DESCRIPTOR =
new ContentDescriptor(ContentDescriptor.RAW_RTP);
Now that we have a Processor
in the Realized state, we need to set up the DataSink
to be able to actually broadcast the RTP media. Creating the DataSink
is simply a matter of making another call to the Manager
object, as shown below:
dataSink = Manager.createDataSink(mediaProcessor.getDataOutput(),
mediaLocator);
The createDataSink()
method takes the output of our new Processor
(as a DataSource
parameter) and the MediaLocator
object, which we created simultaneously with the MediaTransmitter
object. From this, you can begin to see how our different components are linked together in the process model: we take the outputs from a Processor
and use them as the inputs to other components. For this particular application, the Processor
output is used as an input to the DataSink
, which is then used to transmit media.
At this point we are all but done with setting up our media player for broadcast transmission. We just have to create the DataSource
instance, which we'll use to create our processor (that is, the parameter passed to the setDataSource()
method on our MediaTransmitter
). Here's the code to create the DataSource
instance:
File mediaFile = new File(args[1]);
DataSource source = Manager.createDataSource(new MediaLocator(
mediaFile.toURL()));
This code is from the main()
method on the MediaTransmitter
object. Here we create a File
object from the second argument passed in through the command line. We create a MediaLocator
from the file, and subsequently create a DataSource
from the locator. This newly created DataSource
is a reference to the input file for the transmitter. We can then use this DataSource
to initialize the transmitter.
We start the MediaTransmitter
by calling the startTransmitting()
method on it, as shown here:
public void startTransmitting() throws IOException {
mediaProcessor.start();
dataSink.open();
dataSink.start();
}
This method starts the processor first, then opens and starts the DataSink
. After this call, the receiving machines should be able to listen in on the media transmitter.
Stopping the transmitter is just as simple. The following method call stops and closes both the DataSink
and the Processor
:
public void stopTransmitting() throws IOException {
dataSink.stop();
dataSink.close();
mediaProcessor.stop();
mediaProcessor.close();
}
Compile the example application by typing javac MediaTransmitter.java
at a command prompt to create a .class file of the same name in your working directory.
To run the example application, type the following at a command prompt:
java MediaTransmitter rtpMediaLocator audioFile
This example should create a media broadcast of the myAudio.mp3 file. Don't forget to replace rtpMediaLocator with an RTP URL for the media transmission, as discussed earlier. You should also replace audioFile with the file name of an audio file on your local system. Any relative file names will be resolved relative to the current working directory. You should see some messages indicating what file is being played. To stop playing, press the Enter key.
An example command-line interaction for the transmitter is:
java MediaTransmitter rtp://192.168.1.255:49150/audio myAudio.mp3
If initial compilation failed, check to make sure that the JMF jar files are included in the current CLASSPATH
environment variable. Refer to the MediaTransmitter source code to further explore this application and exercise.
Now, you may ask, "What good is it to broadcast media if nobody can see or listen to it?" Fortunately, setting up a client to receive broadcast media requires only a very small change to the MediaPlayerFrame source code of our second example application.
The MediaPlayerFrame
class needs one minor tweak to be able to receive and play the transmitted audio media. In the main()
method, you should comment out the following line:
mpf.setMediaLocator(new MediaLocator(new File(args[0]).toURL()));
And uncomment the following line:
mpf.setMediaLocator(new MediaLocator(args[0]));
This simple change allows us to create a MediaLocator
object using the passed-in String
, rather than creating a File
reference and using that to create the MediaLocator
. All other code remains the same.
Refer to Compiling and running the MediaPlayerFrame for instructions on how to compile and run the MediaPlayerFrame
example application. The only difference is that now you need to specify the RTP URL for the transmitter. An example command-line interaction for the receiver is:
java MediaPlayerFrame rtp://192.168.1.255:49150/audio
If you only have access to one machine on a network, you can still run the transmitter application. When you start the transmitter, you may either use a broadcast address for the RTP URL, or specify the machine address for the machine you're working on. In order to be able to tune into the transmission, the receiver must use the exact same RTP URL upon being started.
If you're running a truly networked version of these examples, each machine you use needs to have JMF installed in order to either transmit or receive streamed media. This is necessary because both the transmitter and receiver applications make heavy use of JMF APIs.
In either case, be sure to use the same address
and port
parameters in the RTP URL specification; otherwise the media transmission will not work.
I hope this tutorial has offered you an exciting glimpse of what you can do with the JMF API. We have created three small applications to play local audio and video, as well as broadcast and receive media over a network. The source code for these applications contains many javadoc-style comments. These should help to answer any remaining questions you have.
Many JMF topics were not covered in this tutorial. Rather, we've focused on the basic concepts and applications of JMF; with this foundation, you can more easily branch out into other areas of study. To get started on deeper applications of JMF, you may want to follow up on some of the advanced topics mentioned in the next section. For further reading on any of the topics covered in this tutorial, refer to Related topics .
A number of worthwhile exercises were beyond the scope of this tutorial. With the brief introductions below and further study on your own, you could extend the source for our three applications, and also expand your knowledge of how JMF works. Try the following exercises to get started:
javax.media.CaptureDeviceManager
class and the javax.media.protocol.CaptureDevice
interface APIs. For an advanced exercise, consider working with CaptureDeviceManager
and the CaptureDevice
interface to add media capture functionality to the GUI version of the media player application. javax.media.DataSink
output. Another output representation is with the javax.media.rtp.SessionManager
. This manager class allows clients to create and monitor their own RTP streams and connections. Through the SessionManager
and the subsequently created streams, it is possible to more closely monitor RTP sessions. For an advanced exercise, convert our third demo application to use the SessionManager
, and then monitor the outgoing RTP streams and which clients are tuning in. Format
and ContentDescriptor
classes to get a better idea of how this is done. MediaLocator
of the DataSink
object to a file rather than a URL. You will find this topic discussed in more depth in the JMF User's Guide. 翻译自: https://www.ibm.com/developerworks/java/tutorials/j-jmf/j-jmf.html