<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/css" href="http://commons.oreilly.com/wiki/skins/common/feed.css?97"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
		<id>http://commons.oreilly.com/wiki/index.php?title=QuickTime_for_Java:_A_Developer's_Notebook/Capture&amp;action=history&amp;feed=atom</id>
		<title>QuickTime for Java: A Developer's Notebook/Capture - Revision history</title>
		<link rel="self" type="application/atom+xml" href="http://commons.oreilly.com/wiki/index.php?title=QuickTime_for_Java:_A_Developer's_Notebook/Capture&amp;action=history&amp;feed=atom"/>
		<link rel="alternate" type="text/html" href="http://commons.oreilly.com/wiki/index.php?title=QuickTime_for_Java:_A_Developer%27s_Notebook/Capture&amp;action=history"/>
		<updated>2013-05-20T04:09:10Z</updated>
		<subtitle>Revision history for this page on the wiki</subtitle>
		<generator>MediaWiki 1.11.0</generator>

	<entry>
		<id>http://commons.oreilly.com/wiki/index.php?title=QuickTime_for_Java:_A_Developer%27s_Notebook/Capture&amp;diff=7241&amp;oldid=prev</id>
		<title>Docbook2Wiki: Initial conversion from Docbook</title>
		<link rel="alternate" type="text/html" href="http://commons.oreilly.com/wiki/index.php?title=QuickTime_for_Java:_A_Developer%27s_Notebook/Capture&amp;diff=7241&amp;oldid=prev"/>
				<updated>2008-03-07T13:22:47Z</updated>
		
		<summary type="html">&lt;p&gt;Initial conversion from Docbook&lt;/p&gt;

			&lt;table style=&quot;background-color: white; color:black;&quot;&gt;
			&lt;col class='diff-marker' /&gt;
			&lt;col class='diff-content' /&gt;
			&lt;col class='diff-marker' /&gt;
			&lt;col class='diff-content' /&gt;
			&lt;tr&gt;
				&lt;td colspan='2' style=&quot;background-color: white; color:black;&quot;&gt;←Older revision&lt;/td&gt;
				&lt;td colspan='2' style=&quot;background-color: white; color:black;&quot;&gt;Revision as of 13:22, 7 March 2008&lt;/td&gt;
			&lt;/tr&gt;
		&lt;/table&gt;</summary>
		<author><name>Docbook2Wiki</name></author>	</entry>

	<entry>
		<id>http://commons.oreilly.com/wiki/index.php?title=QuickTime_for_Java:_A_Developer%27s_Notebook/Capture&amp;diff=7162&amp;oldid=prev</id>
		<title>Docbook2Wiki: Initial conversion from Docbook</title>
		<link rel="alternate" type="text/html" href="http://commons.oreilly.com/wiki/index.php?title=QuickTime_for_Java:_A_Developer%27s_Notebook/Capture&amp;diff=7162&amp;oldid=prev"/>
				<updated>2008-03-07T13:17:34Z</updated>
		
		<summary type="html">&lt;p&gt;Initial conversion from Docbook&lt;/p&gt;

			&lt;table style=&quot;background-color: white; color:black;&quot;&gt;
			&lt;col class='diff-marker' /&gt;
			&lt;col class='diff-content' /&gt;
			&lt;col class='diff-marker' /&gt;
			&lt;col class='diff-content' /&gt;
			&lt;tr&gt;
				&lt;td colspan='2' style=&quot;background-color: white; color:black;&quot;&gt;←Older revision&lt;/td&gt;
				&lt;td colspan='2' style=&quot;background-color: white; color:black;&quot;&gt;Revision as of 13:17, 7 March 2008&lt;/td&gt;
			&lt;/tr&gt;
		&lt;/table&gt;</summary>
		<author><name>Docbook2Wiki</name></author>	</entry>

	<entry>
		<id>http://commons.oreilly.com/wiki/index.php?title=QuickTime_for_Java:_A_Developer%27s_Notebook/Capture&amp;diff=4465&amp;oldid=prev</id>
		<title>Evanlenz: 1 revision(s)</title>
		<link rel="alternate" type="text/html" href="http://commons.oreilly.com/wiki/index.php?title=QuickTime_for_Java:_A_Developer%27s_Notebook/Capture&amp;diff=4465&amp;oldid=prev"/>
				<updated>2008-03-07T00:25:59Z</updated>
		
		<summary type="html">&lt;p&gt;1 revision(s)&lt;/p&gt;

			&lt;table style=&quot;background-color: white; color:black;&quot;&gt;
			&lt;col class='diff-marker' /&gt;
			&lt;col class='diff-content' /&gt;
			&lt;col class='diff-marker' /&gt;
			&lt;col class='diff-content' /&gt;
			&lt;tr&gt;
				&lt;td colspan='2' style=&quot;background-color: white; color:black;&quot;&gt;←Older revision&lt;/td&gt;
				&lt;td colspan='2' style=&quot;background-color: white; color:black;&quot;&gt;Revision as of 00:25, 7 March 2008&lt;/td&gt;
			&lt;/tr&gt;
		&lt;/table&gt;</summary>
		<author><name>Evanlenz</name></author>	</entry>

	<entry>
		<id>http://commons.oreilly.com/wiki/index.php?title=QuickTime_for_Java:_A_Developer%27s_Notebook/Capture&amp;diff=4464&amp;oldid=prev</id>
		<title>Docbook2Wiki: Initial conversion from Docbook</title>
		<link rel="alternate" type="text/html" href="http://commons.oreilly.com/wiki/index.php?title=QuickTime_for_Java:_A_Developer%27s_Notebook/Capture&amp;diff=4464&amp;oldid=prev"/>
				<updated>2008-03-07T00:17:35Z</updated>
		
		<summary type="html">&lt;p&gt;Initial conversion from Docbook&lt;/p&gt;

			&lt;table style=&quot;background-color: white; color:black;&quot;&gt;
			&lt;col class='diff-marker' /&gt;
			&lt;col class='diff-content' /&gt;
			&lt;col class='diff-marker' /&gt;
			&lt;col class='diff-content' /&gt;
			&lt;tr&gt;
				&lt;td colspan='2' style=&quot;background-color: white; color:black;&quot;&gt;←Older revision&lt;/td&gt;
				&lt;td colspan='2' style=&quot;background-color: white; color:black;&quot;&gt;Revision as of 00:17, 7 March 2008&lt;/td&gt;
			&lt;/tr&gt;
		&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot;&gt;Line 162:&lt;/td&gt;
&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot;&gt;Line 162:&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class='diff-marker'&gt; &lt;/td&gt;&lt;td style=&quot;background: #eee; color:black; font-size: smaller;&quot;&gt;&lt;div&gt;'''Figure 6-1. Audio capture preview window'''&lt;/div&gt;&lt;/td&gt;&lt;td class='diff-marker'&gt; &lt;/td&gt;&lt;td style=&quot;background: #eee; color:black; font-size: smaller;&quot;&gt;&lt;div&gt;'''Figure 6-1. Audio capture preview window'''&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class='diff-marker'&gt; &lt;/td&gt;&lt;td style=&quot;background: #eee; color:black; font-size: smaller;&quot;&gt;&lt;/td&gt;&lt;td class='diff-marker'&gt; &lt;/td&gt;&lt;td style=&quot;background: #eee; color:black; font-size: smaller;&quot;&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class='diff-marker'&gt;-&lt;/td&gt;&lt;td style=&quot;background: #ffa; color:black; font-size: smaller;&quot;&gt;&lt;div&gt;[[Image:QuickTime for Java: A &lt;del style=&quot;color: red; font-weight: bold; text-decoration: none;&quot;&gt;Developer's &lt;/del&gt;Notebook_I_6_tt80.png|Audio capture preview window]]&lt;/div&gt;&lt;/td&gt;&lt;td class='diff-marker'&gt;+&lt;/td&gt;&lt;td style=&quot;background: #cfc; color:black; font-size: smaller;&quot;&gt;&lt;div&gt;[[Image:QuickTime for Java: A &lt;ins style=&quot;color: red; font-weight: bold; text-decoration: none;&quot;&gt;Developers &lt;/ins&gt;Notebook_I_6_tt80.png|Audio capture preview window]]&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class='diff-marker'&gt; &lt;/td&gt;&lt;td style=&quot;background: #eee; color:black; font-size: smaller;&quot;&gt;&lt;div&gt;&amp;lt;/div&amp;gt;&lt;/div&gt;&lt;/td&gt;&lt;td class='diff-marker'&gt; &lt;/td&gt;&lt;td style=&quot;background: #eee; color:black; font-size: smaller;&quot;&gt;&lt;div&gt;&amp;lt;/div&amp;gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class='diff-marker'&gt; &lt;/td&gt;&lt;td style=&quot;background: #eee; color:black; font-size: smaller;&quot;&gt;&lt;/td&gt;&lt;td class='diff-marker'&gt; &lt;/td&gt;&lt;td style=&quot;background: #eee; color:black; font-size: smaller;&quot;&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot;&gt;Line 259:&lt;/td&gt;
&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot;&gt;Line 259:&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class='diff-marker'&gt; &lt;/td&gt;&lt;td style=&quot;background: #eee; color:black; font-size: smaller;&quot;&gt;&lt;div&gt;'''Figure 6-2. Discovering and displaying audio capture devices'''&lt;/div&gt;&lt;/td&gt;&lt;td class='diff-marker'&gt; &lt;/td&gt;&lt;td style=&quot;background: #eee; color:black; font-size: smaller;&quot;&gt;&lt;div&gt;'''Figure 6-2. Discovering and displaying audio capture devices'''&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class='diff-marker'&gt; &lt;/td&gt;&lt;td style=&quot;background: #eee; color:black; font-size: smaller;&quot;&gt;&lt;/td&gt;&lt;td class='diff-marker'&gt; &lt;/td&gt;&lt;td style=&quot;background: #eee; color:black; font-size: smaller;&quot;&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class='diff-marker'&gt;-&lt;/td&gt;&lt;td style=&quot;background: #ffa; color:black; font-size: smaller;&quot;&gt;&lt;div&gt;[[Image:QuickTime for Java: A &lt;del style=&quot;color: red; font-weight: bold; text-decoration: none;&quot;&gt;Developer's &lt;/del&gt;Notebook_I_6_tt83.png|Discovering and displaying audio capture devices]]&lt;/div&gt;&lt;/td&gt;&lt;td class='diff-marker'&gt;+&lt;/td&gt;&lt;td style=&quot;background: #cfc; color:black; font-size: smaller;&quot;&gt;&lt;div&gt;[[Image:QuickTime for Java: A &lt;ins style=&quot;color: red; font-weight: bold; text-decoration: none;&quot;&gt;Developers &lt;/ins&gt;Notebook_I_6_tt83.png|Discovering and displaying audio capture devices]]&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class='diff-marker'&gt; &lt;/td&gt;&lt;td style=&quot;background: #eee; color:black; font-size: smaller;&quot;&gt;&lt;div&gt;&amp;lt;/div&amp;gt;&lt;/div&gt;&lt;/td&gt;&lt;td class='diff-marker'&gt; &lt;/td&gt;&lt;td style=&quot;background: #eee; color:black; font-size: smaller;&quot;&gt;&lt;div&gt;&amp;lt;/div&amp;gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class='diff-marker'&gt; &lt;/td&gt;&lt;td style=&quot;background: #eee; color:black; font-size: smaller;&quot;&gt;&lt;/td&gt;&lt;td class='diff-marker'&gt; &lt;/td&gt;&lt;td style=&quot;background: #eee; color:black; font-size: smaller;&quot;&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot;&gt;Line 364:&lt;/td&gt;
&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot;&gt;Line 364:&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class='diff-marker'&gt; &lt;/td&gt;&lt;td style=&quot;background: #eee; color:black; font-size: smaller;&quot;&gt;&lt;div&gt;'''Figure 6-3. Audio channel settings dialog'''&lt;/div&gt;&lt;/td&gt;&lt;td class='diff-marker'&gt; &lt;/td&gt;&lt;td style=&quot;background: #eee; color:black; font-size: smaller;&quot;&gt;&lt;div&gt;'''Figure 6-3. Audio channel settings dialog'''&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class='diff-marker'&gt; &lt;/td&gt;&lt;td style=&quot;background: #eee; color:black; font-size: smaller;&quot;&gt;&lt;/td&gt;&lt;td class='diff-marker'&gt; &lt;/td&gt;&lt;td style=&quot;background: #eee; color:black; font-size: smaller;&quot;&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class='diff-marker'&gt;-&lt;/td&gt;&lt;td style=&quot;background: #ffa; color:black; font-size: smaller;&quot;&gt;&lt;div&gt;[[Image:QuickTime for Java: A &lt;del style=&quot;color: red; font-weight: bold; text-decoration: none;&quot;&gt;Developer's &lt;/del&gt;Notebook_I_6_tt91.png|Audio channel settings dialog]]&lt;/div&gt;&lt;/td&gt;&lt;td class='diff-marker'&gt;+&lt;/td&gt;&lt;td style=&quot;background: #cfc; color:black; font-size: smaller;&quot;&gt;&lt;div&gt;[[Image:QuickTime for Java: A &lt;ins style=&quot;color: red; font-weight: bold; text-decoration: none;&quot;&gt;Developers &lt;/ins&gt;Notebook_I_6_tt91.png|Audio channel settings dialog]]&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class='diff-marker'&gt; &lt;/td&gt;&lt;td style=&quot;background: #eee; color:black; font-size: smaller;&quot;&gt;&lt;div&gt;&amp;lt;/div&amp;gt;&lt;/div&gt;&lt;/td&gt;&lt;td class='diff-marker'&gt; &lt;/td&gt;&lt;td style=&quot;background: #eee; color:black; font-size: smaller;&quot;&gt;&lt;div&gt;&amp;lt;/div&amp;gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class='diff-marker'&gt; &lt;/td&gt;&lt;td style=&quot;background: #eee; color:black; font-size: smaller;&quot;&gt;&lt;/td&gt;&lt;td class='diff-marker'&gt; &lt;/td&gt;&lt;td style=&quot;background: #eee; color:black; font-size: smaller;&quot;&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot;&gt;Line 550:&lt;/td&gt;
&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot;&gt;Line 550:&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class='diff-marker'&gt; &lt;/td&gt;&lt;td style=&quot;background: #eee; color:black; font-size: smaller;&quot;&gt;&lt;div&gt;'''Figure 6-4. Video channel settings dialog'''&lt;/div&gt;&lt;/td&gt;&lt;td class='diff-marker'&gt; &lt;/td&gt;&lt;td style=&quot;background: #eee; color:black; font-size: smaller;&quot;&gt;&lt;div&gt;'''Figure 6-4. Video channel settings dialog'''&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class='diff-marker'&gt; &lt;/td&gt;&lt;td style=&quot;background: #eee; color:black; font-size: smaller;&quot;&gt;&lt;/td&gt;&lt;td class='diff-marker'&gt; &lt;/td&gt;&lt;td style=&quot;background: #eee; color:black; font-size: smaller;&quot;&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class='diff-marker'&gt;-&lt;/td&gt;&lt;td style=&quot;background: #ffa; color:black; font-size: smaller;&quot;&gt;&lt;div&gt;[[Image:QuickTime for Java: A &lt;del style=&quot;color: red; font-weight: bold; text-decoration: none;&quot;&gt;Developer's &lt;/del&gt;Notebook_I_6_tt92.png|Video channel settings dialog]]&lt;/div&gt;&lt;/td&gt;&lt;td class='diff-marker'&gt;+&lt;/td&gt;&lt;td style=&quot;background: #cfc; color:black; font-size: smaller;&quot;&gt;&lt;div&gt;[[Image:QuickTime for Java: A &lt;ins style=&quot;color: red; font-weight: bold; text-decoration: none;&quot;&gt;Developers &lt;/ins&gt;Notebook_I_6_tt92.png|Video channel settings dialog]]&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class='diff-marker'&gt; &lt;/td&gt;&lt;td style=&quot;background: #eee; color:black; font-size: smaller;&quot;&gt;&lt;div&gt;&amp;lt;/div&amp;gt;&lt;/div&gt;&lt;/td&gt;&lt;td class='diff-marker'&gt; &lt;/td&gt;&lt;td style=&quot;background: #eee; color:black; font-size: smaller;&quot;&gt;&lt;div&gt;&amp;lt;/div&amp;gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class='diff-marker'&gt; &lt;/td&gt;&lt;td style=&quot;background: #eee; color:black; font-size: smaller;&quot;&gt;&lt;/td&gt;&lt;td class='diff-marker'&gt; &lt;/td&gt;&lt;td style=&quot;background: #eee; color:black; font-size: smaller;&quot;&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot;&gt;Line 564:&lt;/td&gt;
&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot;&gt;Line 564:&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class='diff-marker'&gt; &lt;/td&gt;&lt;td style=&quot;background: #eee; color:black; font-size: smaller;&quot;&gt;&lt;div&gt;'''Figure 6-5. Captured video playing in a window'''&lt;/div&gt;&lt;/td&gt;&lt;td class='diff-marker'&gt; &lt;/td&gt;&lt;td style=&quot;background: #eee; color:black; font-size: smaller;&quot;&gt;&lt;div&gt;'''Figure 6-5. Captured video playing in a window'''&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class='diff-marker'&gt; &lt;/td&gt;&lt;td style=&quot;background: #eee; color:black; font-size: smaller;&quot;&gt;&lt;/td&gt;&lt;td class='diff-marker'&gt; &lt;/td&gt;&lt;td style=&quot;background: #eee; color:black; font-size: smaller;&quot;&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class='diff-marker'&gt;-&lt;/td&gt;&lt;td style=&quot;background: #ffa; color:black; font-size: smaller;&quot;&gt;&lt;div&gt;[[Image:QuickTime for Java: A &lt;del style=&quot;color: red; font-weight: bold; text-decoration: none;&quot;&gt;Developer's &lt;/del&gt;Notebook_I_6_tt93.png|Captured video playing in a window]]&lt;/div&gt;&lt;/td&gt;&lt;td class='diff-marker'&gt;+&lt;/td&gt;&lt;td style=&quot;background: #cfc; color:black; font-size: smaller;&quot;&gt;&lt;div&gt;[[Image:QuickTime for Java: A &lt;ins style=&quot;color: red; font-weight: bold; text-decoration: none;&quot;&gt;Developers &lt;/ins&gt;Notebook_I_6_tt93.png|Captured video playing in a window]]&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class='diff-marker'&gt; &lt;/td&gt;&lt;td style=&quot;background: #eee; color:black; font-size: smaller;&quot;&gt;&lt;div&gt;&amp;lt;/div&amp;gt;&lt;/div&gt;&lt;/td&gt;&lt;td class='diff-marker'&gt; &lt;/td&gt;&lt;td style=&quot;background: #eee; color:black; font-size: smaller;&quot;&gt;&lt;div&gt;&amp;lt;/div&amp;gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class='diff-marker'&gt; &lt;/td&gt;&lt;td style=&quot;background: #eee; color:black; font-size: smaller;&quot;&gt;&lt;/td&gt;&lt;td class='diff-marker'&gt; &lt;/td&gt;&lt;td style=&quot;background: #eee; color:black; font-size: smaller;&quot;&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot;&gt;Line 845:&lt;/td&gt;
&lt;td colspan=&quot;2&quot; class=&quot;diff-lineno&quot;&gt;Line 845:&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class='diff-marker'&gt; &lt;/td&gt;&lt;td style=&quot;background: #eee; color:black; font-size: smaller;&quot;&gt;&lt;div&gt;'''Figure 6-6. Video motion detector window'''&lt;/div&gt;&lt;/td&gt;&lt;td class='diff-marker'&gt; &lt;/td&gt;&lt;td style=&quot;background: #eee; color:black; font-size: smaller;&quot;&gt;&lt;div&gt;'''Figure 6-6. Video motion detector window'''&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class='diff-marker'&gt; &lt;/td&gt;&lt;td style=&quot;background: #eee; color:black; font-size: smaller;&quot;&gt;&lt;/td&gt;&lt;td class='diff-marker'&gt; &lt;/td&gt;&lt;td style=&quot;background: #eee; color:black; font-size: smaller;&quot;&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class='diff-marker'&gt;-&lt;/td&gt;&lt;td style=&quot;background: #ffa; color:black; font-size: smaller;&quot;&gt;&lt;div&gt;[[Image:QuickTime for Java: A &lt;del style=&quot;color: red; font-weight: bold; text-decoration: none;&quot;&gt;Developer's &lt;/del&gt;Notebook_I_6_tt97.png|Video motion detector window]]&lt;/div&gt;&lt;/td&gt;&lt;td class='diff-marker'&gt;+&lt;/td&gt;&lt;td style=&quot;background: #cfc; color:black; font-size: smaller;&quot;&gt;&lt;div&gt;[[Image:QuickTime for Java: A &lt;ins style=&quot;color: red; font-weight: bold; text-decoration: none;&quot;&gt;Developers &lt;/ins&gt;Notebook_I_6_tt97.png|Video motion detector window]]&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class='diff-marker'&gt; &lt;/td&gt;&lt;td style=&quot;background: #eee; color:black; font-size: smaller;&quot;&gt;&lt;div&gt;&amp;lt;/div&amp;gt;&lt;/div&gt;&lt;/td&gt;&lt;td class='diff-marker'&gt; &lt;/td&gt;&lt;td style=&quot;background: #eee; color:black; font-size: smaller;&quot;&gt;&lt;div&gt;&amp;lt;/div&amp;gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td class='diff-marker'&gt; &lt;/td&gt;&lt;td style=&quot;background: #eee; color:black; font-size: smaller;&quot;&gt;&lt;/td&gt;&lt;td class='diff-marker'&gt; &lt;/td&gt;&lt;td style=&quot;background: #eee; color:black; font-size: smaller;&quot;&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;</summary>
		<author><name>Docbook2Wiki</name></author>	</entry>

	<entry>
		<id>http://commons.oreilly.com/wiki/index.php?title=QuickTime_for_Java:_A_Developer%27s_Notebook/Capture&amp;diff=4313&amp;oldid=prev</id>
		<title>Evanlenz: 1 revision(s)</title>
		<link rel="alternate" type="text/html" href="http://commons.oreilly.com/wiki/index.php?title=QuickTime_for_Java:_A_Developer%27s_Notebook/Capture&amp;diff=4313&amp;oldid=prev"/>
				<updated>2008-03-07T00:16:21Z</updated>
		
		<summary type="html">&lt;p&gt;1 revision(s)&lt;/p&gt;

			&lt;table style=&quot;background-color: white; color:black;&quot;&gt;
			&lt;col class='diff-marker' /&gt;
			&lt;col class='diff-content' /&gt;
			&lt;col class='diff-marker' /&gt;
			&lt;col class='diff-content' /&gt;
			&lt;tr&gt;
				&lt;td colspan='2' style=&quot;background-color: white; color:black;&quot;&gt;←Older revision&lt;/td&gt;
				&lt;td colspan='2' style=&quot;background-color: white; color:black;&quot;&gt;Revision as of 00:16, 7 March 2008&lt;/td&gt;
			&lt;/tr&gt;
		&lt;/table&gt;</summary>
		<author><name>Evanlenz</name></author>	</entry>

	<entry>
		<id>http://commons.oreilly.com/wiki/index.php?title=QuickTime_for_Java:_A_Developer%27s_Notebook/Capture&amp;diff=4312&amp;oldid=prev</id>
		<title>Docbook2Wiki: Initial conversion from Docbook</title>
		<link rel="alternate" type="text/html" href="http://commons.oreilly.com/wiki/index.php?title=QuickTime_for_Java:_A_Developer%27s_Notebook/Capture&amp;diff=4312&amp;oldid=prev"/>
				<updated>2008-03-07T00:15:32Z</updated>
		
		<summary type="html">&lt;p&gt;Initial conversion from Docbook&lt;/p&gt;

			&lt;table style=&quot;background-color: white; color:black;&quot;&gt;
			&lt;col class='diff-marker' /&gt;
			&lt;col class='diff-content' /&gt;
			&lt;col class='diff-marker' /&gt;
			&lt;col class='diff-content' /&gt;
			&lt;tr&gt;
				&lt;td colspan='2' style=&quot;background-color: white; color:black;&quot;&gt;←Older revision&lt;/td&gt;
				&lt;td colspan='2' style=&quot;background-color: white; color:black;&quot;&gt;Revision as of 00:15, 7 March 2008&lt;/td&gt;
			&lt;/tr&gt;
		&lt;/table&gt;</summary>
		<author><name>Docbook2Wiki</name></author>	</entry>

	<entry>
		<id>http://commons.oreilly.com/wiki/index.php?title=QuickTime_for_Java:_A_Developer%27s_Notebook/Capture&amp;diff=4161&amp;oldid=prev</id>
		<title>Evanlenz: 1 revision(s)</title>
		<link rel="alternate" type="text/html" href="http://commons.oreilly.com/wiki/index.php?title=QuickTime_for_Java:_A_Developer%27s_Notebook/Capture&amp;diff=4161&amp;oldid=prev"/>
				<updated>2008-03-06T23:57:57Z</updated>
		
		<summary type="html">&lt;p&gt;1 revision(s)&lt;/p&gt;

			&lt;table style=&quot;background-color: white; color:black;&quot;&gt;
			&lt;col class='diff-marker' /&gt;
			&lt;col class='diff-content' /&gt;
			&lt;col class='diff-marker' /&gt;
			&lt;col class='diff-content' /&gt;
			&lt;tr&gt;
				&lt;td colspan='2' style=&quot;background-color: white; color:black;&quot;&gt;←Older revision&lt;/td&gt;
				&lt;td colspan='2' style=&quot;background-color: white; color:black;&quot;&gt;Revision as of 23:57, 6 March 2008&lt;/td&gt;
			&lt;/tr&gt;
		&lt;/table&gt;</summary>
		<author><name>Evanlenz</name></author>	</entry>

	<entry>
		<id>http://commons.oreilly.com/wiki/index.php?title=QuickTime_for_Java:_A_Developer%27s_Notebook/Capture&amp;diff=4160&amp;oldid=prev</id>
		<title>Docbook2Wiki: Initial conversion from Docbook</title>
		<link rel="alternate" type="text/html" href="http://commons.oreilly.com/wiki/index.php?title=QuickTime_for_Java:_A_Developer%27s_Notebook/Capture&amp;diff=4160&amp;oldid=prev"/>
				<updated>2008-03-06T23:50:56Z</updated>
		
		<summary type="html">&lt;p&gt;Initial conversion from Docbook&lt;/p&gt;
&lt;p&gt;&lt;b&gt;New page&lt;/b&gt;&lt;/p&gt;&lt;div&gt;{{QuickTime for Java: A Developer's Notebook/TOC}}&lt;br /&gt;
Much of this book has assumed you already had media of some kind to play and edit—but where does this media come from in the first place? Digital media has to come from one of two places: either it's completely synthetic or it's ''captured'' from a real-world source. Capture, via devices like microphones and video cameras, is far more common.&lt;br /&gt;
&lt;br /&gt;
The problem is that capture doesn't officially work in QuickTime for Java. The problem dates back to Apple's Java 1.4.1 rearchitecture, which broke QTJ and forced massive changes to the API in QTJ 6.1. One of the things that was not updated for QTJ was the ability to get an on-screen component from a &amp;lt;tt&amp;gt;SequenceGrabber&amp;lt;/tt&amp;gt;, which is the QuickTime capture component. Instead, Apple just put a statement in the QTJ 6.1 documentation:&lt;br /&gt;
&lt;br /&gt;
Although sequence grabbing is currently not supported in QuickTime for Java 1.4.1, it may be provided in future releases.&lt;br /&gt;
&lt;br /&gt;
But if you think back to how the QTJ 6.1 situation was described in [[QuickTime for Java: A Developer's Notebook/Getting Up and Running with QuickTime for Java|Chapter 1]], you might recall that QTJ classes that didn't require working with AWT—such as the &amp;lt;tt&amp;gt;quicktime.std&amp;lt;/tt&amp;gt; classes that simply wrapped straight-C calls—were unaffected by the Java 1.4.1 changes and still worked. Given that, notice in the Javadoc the package called &amp;lt;tt&amp;gt;quicktime.std.sg&amp;lt;/tt&amp;gt;, which contains the &amp;lt;tt&amp;gt;SequenceGrabber&amp;lt;/tt&amp;gt; class among several others. Besides, capture, per se, doesn't necessarily imply using the screen, so shouldn't it still work?&lt;br /&gt;
&lt;br /&gt;
The good news is that it ''does''. In this chapter, I'll introduce the parts of the capture API that still work in QTJ, even without official support: capturing audio, capturing to disk, and even getting captured video on screen with a little QuickDraw voodoo. QTJ still needs proper support for on-screen, video-capture preview, but there's plenty to do in the meantime.&lt;br /&gt;
&lt;br /&gt;
== Capturing and Previewing Audio ==&lt;br /&gt;
&lt;br /&gt;
Audio capture is a good place to start because that sidesteps the problem of the broken video preview. There's plenty to be learned in just opening up the default microphone and looking at the incoming ''level data''—that is, how loud or soft the incoming sound data is.&lt;br /&gt;
&lt;br /&gt;
=== How do I do that? ===&lt;br /&gt;
&lt;br /&gt;
Setting up audio capture requires a number of steps. You start by constructing a &amp;lt;tt&amp;gt;SequenceGrabber&amp;lt;/tt&amp;gt;. This object coordinates all the capture channels (audio, video...even text capture), and allows you to set capture parameters like whether to save the captured data to disk, set a maximum amount of time to let the capture run, etc.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;note&amp;quot;&amp;gt;&lt;br /&gt;
'''Note'''&lt;br /&gt;
&lt;br /&gt;
''Don't scoff—there really are text-capture devices. For example, you could capture the closed captions off regular TV (also called &amp;quot;line 21&amp;quot; data)''.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Once you have the &amp;lt;tt&amp;gt;SequenceGrabber&amp;lt;/tt&amp;gt;, you use an optional &amp;lt;tt&amp;gt;prepare( )&amp;lt;/tt&amp;gt; call to indicate whether you intend to preview the captured media, record it to disk, or both.&lt;br /&gt;
&lt;br /&gt;
To work with sound, you need to create a sound channel, by calling the &amp;lt;tt&amp;gt;SGSoundChannel&amp;lt;/tt&amp;gt; constructor and passing in the &amp;lt;tt&amp;gt;SequenceGrabber&amp;lt;/tt&amp;gt;. This object allows you to configure the audio capture, choose among audio capture devices (see the next lab), and get the device's driver. The driver, exposed by the &amp;lt;tt&amp;gt;SPBDevice&amp;lt;/tt&amp;gt; class, provides methods for checking the input line level.&lt;br /&gt;
&lt;br /&gt;
As an example, compile and run the &amp;lt;tt&amp;gt;AudioCapturePreview&amp;lt;/tt&amp;gt; application as shown in [[QuickTime for Java: A Developer's Notebook/Capture#quicktimejvaadn-CHP-6-EX-1|Example 6-1]]. Note that you need to have at least one audio capture device hooked up to your computer. Most Macs come with a built-in microphone. If you don't have one, you can use a USB capture device (like a headset or external microphone) or a FireWire device (like an iSight).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;note&amp;quot;&amp;gt;&lt;br /&gt;
'''Note'''&lt;br /&gt;
&lt;br /&gt;
''Compile and run this example from the book's downloadable code with ant run-ch06-audiocapture-preview''.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;quicktimejvaadn-CHP-6-EX-1&amp;quot;&amp;gt;&lt;br /&gt;
'''Example 6-1. Previewing captured audio'''&lt;br /&gt;
&lt;br /&gt;
 package com.oreilly.qtjnotebook.ch06;&lt;br /&gt;
  &lt;br /&gt;
 import quicktime.*;&lt;br /&gt;
 import quicktime.io.*;&lt;br /&gt;
 import quicktime.std.*;&lt;br /&gt;
 import quicktime.std.sg.*;&lt;br /&gt;
 import quicktime.std.movies.*;&lt;br /&gt;
 import quicktime.std.image.*;&lt;br /&gt;
 import quicktime.qd.*;&lt;br /&gt;
 import quicktime.sound.*;&lt;br /&gt;
 import java.awt.*;&lt;br /&gt;
 import java.awt.event.*;&lt;br /&gt;
 import javax.swing.Timer;&lt;br /&gt;
 import com.oreilly.qtjnotebook.ch01.QTSessionCheck;&lt;br /&gt;
 public class AudioCapturePreview extends Frame &lt;br /&gt;
   implements ItemListener {&lt;br /&gt;
   static final Dimension meterDim = new Dimension (200, 25);&lt;br /&gt;
   Checkbox previewCheck;&lt;br /&gt;
   AudioLevelMeter audioLevelMeter;&lt;br /&gt;
   SequenceGrabber grabber;&lt;br /&gt;
   SGSoundChannel soundChannel;&lt;br /&gt;
   SPBDevice inputDriver;&lt;br /&gt;
   boolean grabbing = true;&lt;br /&gt;
   public AudioCapturePreview( ) throws QTException {&lt;br /&gt;
       super (&amp;quot;Audio Preview&amp;quot;);&lt;br /&gt;
       QTSessionCheck.check( );&lt;br /&gt;
       setLayout (new GridLayout (3, 1));&lt;br /&gt;
       add (new Panel( )); // reserved for next lab&lt;br /&gt;
       previewCheck = new Checkbox (&amp;quot;Preview&amp;quot;, false);&lt;br /&gt;
       previewCheck.addItemListener (this);&lt;br /&gt;
       add (previewCheck);&lt;br /&gt;
       audioLevelMeter = new AudioLevelMeter( );&lt;br /&gt;
       add (audioLevelMeter);&lt;br /&gt;
       // 4th row is reserved for later lab&lt;br /&gt;
       setUpAudioGrab( );&lt;br /&gt;
       grabbing = true;&lt;br /&gt;
   }&lt;br /&gt;
   public void itemStateChanged (ItemEvent e) {&lt;br /&gt;
       try {&lt;br /&gt;
           if (e.getSource( ) =  = previewCheck) {&lt;br /&gt;
               if (previewCheck.getState( ))&lt;br /&gt;
                   soundChannel.setVolume (1.0f);&lt;br /&gt;
               else&lt;br /&gt;
                   soundChannel.setVolume (0.0f);&lt;br /&gt;
           }&lt;br /&gt;
       } catch (QTException qte) {&lt;br /&gt;
           qte.printStackTrace( );&lt;br /&gt;
       }&lt;br /&gt;
   }&lt;br /&gt;
   protected void setUpAudioGrab( ) throws QTException {&lt;br /&gt;
       grabber = new SequenceGrabber( );&lt;br /&gt;
       soundChannel = new SGSoundChannel (grabber);&lt;br /&gt;
       System.out.println (&amp;quot;Got SGAudioChannel&amp;quot;);&lt;br /&gt;
       System.out.println (&amp;quot;SGChannelInfo = &amp;quot; +&lt;br /&gt;
                           soundChannel.getSoundInputParameters( ));&lt;br /&gt;
       System.out.println (&amp;quot;SoundDescription = &amp;quot; + &lt;br /&gt;
                           soundChannel.getSoundDescription( ));&lt;br /&gt;
       // prepare and start previewing&lt;br /&gt;
       grabber.prepare(true,false);&lt;br /&gt;
       soundChannel.setUsage (StdQTConstants.seqGrabPreview);&lt;br /&gt;
       soundChannel.setVolume (0.0f);&lt;br /&gt;
       grabber.startPreview( );&lt;br /&gt;
       inputDriver = soundChannel.getInputDriver( );&lt;br /&gt;
       inputDriver.setLevelMeterOnOff (true);&lt;br /&gt;
       int[  ] levelTest = inputDriver.getActiveLevels( );&lt;br /&gt;
       System.out.println (levelTest.length + &amp;quot; active levels&amp;quot;);&lt;br /&gt;
       // set up thread to update level meter&lt;br /&gt;
       ActionListener timerCallback =&lt;br /&gt;
           new ActionListener( ) {&lt;br /&gt;
               public void actionPerformed(ActionEvent e) {&lt;br /&gt;
                   if (grabbing) {&lt;br /&gt;
                       try {&lt;br /&gt;
                           grabber.idle( );&lt;br /&gt;
                           audioLevelMeter.repaint( );&lt;br /&gt;
                       } catch (QTException qte) {&lt;br /&gt;
                           qte.printStackTrace( );&lt;br /&gt;
                       }&lt;br /&gt;
                   }&lt;br /&gt;
               }&lt;br /&gt;
           };&lt;br /&gt;
       Timer timer = new Timer (50, timerCallback);&lt;br /&gt;
       timer.start( );&lt;br /&gt;
   }&lt;br /&gt;
   public static void main (String[  ] args) {&lt;br /&gt;
       try {&lt;br /&gt;
           Frame f = new AudioCapturePreview( );&lt;br /&gt;
           f.pack( );&lt;br /&gt;
           f.setVisible(true);&lt;br /&gt;
       } catch (QTException qte) {&lt;br /&gt;
           qte.printStackTrace( );&lt;br /&gt;
       }&lt;br /&gt;
   }&lt;br /&gt;
   public class AudioLevelMeter extends Canvas {&lt;br /&gt;
       public void paint (Graphics g) {&lt;br /&gt;
           // get current level if available&lt;br /&gt;
           int level = 0;&lt;br /&gt;
           if (inputDriver != null) {&lt;br /&gt;
               try {&lt;br /&gt;
                   int[  ] levels = inputDriver.getActiveLevels( );&lt;br /&gt;
                   if (levels.length &amp;gt; 0)&lt;br /&gt;
                       level = levels[0];&lt;br /&gt;
               } catch (QTException qte) {&lt;br /&gt;
                   qte.printStackTrace( );&lt;br /&gt;
               }&lt;br /&gt;
           }&lt;br /&gt;
           float levelPercent = level / 256f;&lt;br /&gt;
           System.out.println (level + &amp;quot;, &amp;quot; + levelPercent);&lt;br /&gt;
           // draw box&lt;br /&gt;
           g.setColor (Color.green);&lt;br /&gt;
           g.fillRect (0, 0,&lt;br /&gt;
                       (int) (levelPercent * getWidth( )),&lt;br /&gt;
                       getHeight( ));&lt;br /&gt;
       }&lt;br /&gt;
       public Dimension getPreferredSize( ) { return meterDim; }&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When run, the application brings up a small window with a green bar that indicates the current level on the line, as seen in [[QuickTime for Java: A Developer's Notebook/Capture#quicktimejvaadn-CHP-6-FIG-1|Figure 6-1]]. At maximum input volume—if you're speaking loudly and directly into the microphone—it will stretch all the way to the right of the window.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;quicktimejvaadn-CHP-6-FIG-1&amp;quot;&amp;gt;&lt;br /&gt;
'''Figure 6-1. Audio capture preview window'''&lt;br /&gt;
&lt;br /&gt;
[[Image:QuickTime for Java: A Developer's Notebook_I_6_tt80.png|Audio capture preview window]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
There is also a Preview checkbox that is off initially. Clicking this will play the captured audio over the headset or speakers.&lt;br /&gt;
&lt;br /&gt;
=== What just happened? ===&lt;br /&gt;
&lt;br /&gt;
The constructor does some simple AWT business, adding the Preview checkbox and an &amp;lt;tt&amp;gt;AudioLevelMeter&amp;lt;/tt&amp;gt;, which is an inner class that will be explained shortly. Then it calls &amp;lt;tt&amp;gt;setUpAudioGrab( )&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;setUpAudioGrab( )&amp;lt;/tt&amp;gt; is responsible for initializing the audio capture. As described earlier, the first step is to create a new &amp;lt;tt&amp;gt;SequenceGrabber&amp;lt;/tt&amp;gt; object. Next, tell the grabber what you intend to do with it, via the &amp;lt;tt&amp;gt;prepare( )&amp;lt;/tt&amp;gt; method, which takes two self-explanatory &amp;lt;tt&amp;gt;boolean&amp;lt;/tt&amp;gt;s: &amp;lt;tt&amp;gt;prepareForPreview&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;prepareForRecord&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;tip&amp;quot;&amp;gt;&lt;br /&gt;
'''Tip'''&lt;br /&gt;
&lt;br /&gt;
You don't ''have'' to call &amp;lt;tt&amp;gt;prepare( )&amp;lt;/tt&amp;gt;. If you don't, &amp;lt;tt&amp;gt;SequenceGrabber&amp;lt;/tt&amp;gt; will take care of its setup when you start grabbing, possibly making the startup take longer.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You also need to tell the &amp;lt;tt&amp;gt;SGSoundChannel&amp;lt;/tt&amp;gt; what you want to do via &amp;lt;tt&amp;gt;setUsage( )&amp;lt;/tt&amp;gt;, inherited from &amp;lt;tt&amp;gt;SGChannel&amp;lt;/tt&amp;gt;. As with all methods that take behavior flags, you logically &amp;lt;tt&amp;gt;OR&amp;lt;/tt&amp;gt; together constants to describe your desired usage. In this case, &amp;lt;tt&amp;gt;seqGrabPreview&amp;lt;/tt&amp;gt; indicates that the application is only previewing the captured sound, but you can use (and combine) four other usage constants:&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;tt&amp;gt;seqGrabRecord&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Include this if you want to record the captured media to disk.&lt;br /&gt;
;&amp;lt;tt&amp;gt;seqGrabPlayDuringRecord&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Add this to play while recording.&lt;br /&gt;
;&amp;lt;tt&amp;gt;seqGrabLowLatencyCapture&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Used to get the freshest frame possible (used for video conferencing and live image processing).&lt;br /&gt;
;&amp;lt;tt&amp;gt;seqGrabAlwaysUseTimeBase&amp;lt;/tt&amp;gt;&lt;br /&gt;
: Used by video channels to get more accurate audio/video sync.&lt;br /&gt;
&lt;br /&gt;
At this point, the capture is initialized. Begin capturing audio with &amp;lt;tt&amp;gt;SequenceGrabber&amp;lt;/tt&amp;gt; 's &amp;lt;tt&amp;gt;startPreview( )&amp;lt;/tt&amp;gt; method.&lt;br /&gt;
&lt;br /&gt;
To create the level meter, it's necessary to get an &amp;lt;tt&amp;gt;SPBDevice&amp;lt;/tt&amp;gt;, which provides low-level access to the incoming data. This object provides level meters as an array of &amp;lt;tt&amp;gt;int&amp;lt;/tt&amp;gt;s by first enabling monitoring with &amp;lt;tt&amp;gt;setLevelMeterOnOff(true)&amp;lt;/tt&amp;gt; and then followed by &amp;lt;tt&amp;gt;getActiveLevels( )&amp;lt;/tt&amp;gt;. The returned &amp;lt;tt&amp;gt;int&amp;lt;/tt&amp;gt;s range from 0 (silence), to 255 (maximum input volume). In the example, the &amp;lt;tt&amp;gt;AudioLevelMeter&amp;lt;/tt&amp;gt; inner class gets the first level on each repaint and draws a box whose width is proportional to the audio level. A Swing &amp;lt;tt&amp;gt;Timer&amp;lt;/tt&amp;gt; calls &amp;lt;tt&amp;gt;repaint()&amp;lt;/tt&amp;gt; on the meter every 50 milliseconds to keep it up to date.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;note&amp;quot;&amp;gt;&lt;br /&gt;
'''Note'''&lt;br /&gt;
&lt;br /&gt;
''There may be multiple levels in the array, usually two for stereo input''.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The repaint thread also calls &amp;lt;tt&amp;gt;idle()&amp;lt;/tt&amp;gt; on the &amp;lt;tt&amp;gt;SequenceGrabber&amp;lt;/tt&amp;gt; , which is something you have to call as frequently as possible to give the &amp;lt;tt&amp;gt;SequenceGrabber&amp;lt;/tt&amp;gt; time to operate.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;note&amp;quot;&amp;gt;&lt;br /&gt;
'''Note'''&lt;br /&gt;
&lt;br /&gt;
''SequenceGrabber.idle( ) is a lot like &amp;quot;tasking&amp;quot; back in [[QuickTime for Java: A Developer's Notebook/Playing Movies|Chapter 2]], except there's no convenience class to do it for you''.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== What about... ===&lt;br /&gt;
&lt;br /&gt;
...defaulting the volume off with &amp;lt;tt&amp;gt;SoundChannel.setVolume()&amp;lt;/tt&amp;gt; ? This is a common practice because some users' speakers will be close enough to their microphones to cause feedback when previewing the audio to the speakers. On the other hand, users with headphones probably ''do'' want to hear the preview. So, the best practice is &amp;quot;default off, but let the user turn it on.&amp;quot;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;warning&amp;quot;&amp;gt;&lt;br /&gt;
'''Warning'''&lt;br /&gt;
&lt;br /&gt;
One thing this demo lacks is a call to &amp;lt;tt&amp;gt;SequenceGrabber.stop()&amp;lt;/tt&amp;gt; when the user quits. This is something you should usually do, but I've left it out to make a point. On Mac OS X, if you don't stop the &amp;lt;tt&amp;gt;SequenceGrabber&amp;lt;/tt&amp;gt; and you leave the volume on, you will keep grabbing sound—feedback included—even after the application quits. I've even seen this behavior survive a restart. So, try it out, don't blow your speakers, and then remember to have your programs turn off the volume and call &amp;lt;tt&amp;gt;SequenceGrabber.stop( )&amp;lt;/tt&amp;gt; when they quit.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Selecting Audio Inputs ==&lt;br /&gt;
&lt;br /&gt;
It's not realistic to think the user has only one audio input device. The computer might be connected to a headset for audio conferencing, a webcam for video conferencing, and a camcorder for dumping pictures of the summer vacation into iMovie. Ideally, it should be possible to discover connected devices at runtime and specify which is to be used for capture.&lt;br /&gt;
&lt;br /&gt;
=== How do I do that? ===&lt;br /&gt;
&lt;br /&gt;
To provide a list of devices, you need to query the &amp;lt;tt&amp;gt;SGAudioChannel&amp;lt;/tt&amp;gt; for what devices are available, and then present the choice to the user. So, take the code from the previous lab and add an AWT &amp;lt;tt&amp;gt;Choice&amp;lt;/tt&amp;gt; called &amp;lt;tt&amp;gt;deviceChoice&amp;lt;/tt&amp;gt; in the constructor (replacing a line with a comment that said &amp;quot;reserved for next lab&amp;quot;). Next, after the &amp;lt;tt&amp;gt;SGSoundChannel&amp;lt;/tt&amp;gt; is created in &amp;lt;tt&amp;gt;setUpAudioGrab( )&amp;lt;/tt&amp;gt;, insert this block of code to search for audio devices, adding the name of each to the &amp;lt;tt&amp;gt;deviceChoice&amp;lt;/tt&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
 // create list of input devices&lt;br /&gt;
 SGDeviceList devices = soundChannel.getDeviceList(0);&lt;br /&gt;
 int deviceCount = devices.getCount( );&lt;br /&gt;
 for (int i=0; i&amp;lt;deviceCount; i++) {&lt;br /&gt;
   SGDeviceName deviceName = devices.getDeviceName(i);&lt;br /&gt;
   // is it available?&lt;br /&gt;
   if ((deviceName.getFlags( ) &amp;amp;&lt;br /&gt;
        StdQTConstants.sgDeviceNameFlagDeviceUnavailable) =  = 0)&lt;br /&gt;
   deviceChoice.add(deviceName.getName( ));&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
You need to update the &amp;lt;tt&amp;gt;itemStateChanged()&amp;lt;/tt&amp;gt; callback to handle AWT events on the &amp;lt;tt&amp;gt;deviceChoice&amp;lt;/tt&amp;gt;—in other words, when the user changes the selection. Fortunately, QuickTime allows you to change the input device by passing in a name, so switching devices is pretty easy. Add this to &amp;lt;tt&amp;gt;itemStateChanged( )&amp;lt;/tt&amp;gt;, inside the &amp;lt;tt&amp;gt;try&amp;lt;/tt&amp;gt;-&amp;lt;tt&amp;gt;catch&amp;lt;/tt&amp;gt; block:&lt;br /&gt;
&lt;br /&gt;
 } else if (e.getSource( ) =  = deviceChoice) {&lt;br /&gt;
   System.out.println (&amp;quot;changed device to &amp;quot;+&lt;br /&gt;
                       deviceChoice.getSelectedItem( ));&lt;br /&gt;
   grabbing = false;&lt;br /&gt;
   // grabber.stop( );&lt;br /&gt;
   soundChannel.setDevice (deviceChoice.getSelectedItem( ));&lt;br /&gt;
   // also reset inputDriver&lt;br /&gt;
   inputDriver = soundChannel.getInputDriver( );&lt;br /&gt;
   inputDriver.setLevelMeterOnOff (true);&lt;br /&gt;
  &lt;br /&gt;
   grabbing = true;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
The boolean named &amp;lt;tt&amp;gt;grabbing&amp;lt;/tt&amp;gt; is a simple gate to keep the repaint thread from trying to get levels while this device change is underway, because the old &amp;lt;tt&amp;gt;inputDriver&amp;lt;/tt&amp;gt; will be invalid once the new device is set.&lt;br /&gt;
&lt;br /&gt;
A demo of this technique, &amp;lt;tt&amp;gt;SelectableAudioCapturePreview&amp;lt;/tt&amp;gt;, is shown in [[QuickTime for Java: A Developer's Notebook/Capture#quicktimejvaadn-CHP-6-FIG-2|Figure 6-2]].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;quicktimejvaadn-CHP-6-FIG-2&amp;quot;&amp;gt;&lt;br /&gt;
'''Figure 6-2. Discovering and displaying audio capture devices'''&lt;br /&gt;
&lt;br /&gt;
[[Image:QuickTime for Java: A Developer's Notebook_I_6_tt83.png|Discovering and displaying audio capture devices]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;note&amp;quot;&amp;gt;&lt;br /&gt;
'''Note'''&lt;br /&gt;
&lt;br /&gt;
''Run this example with ant run-ch06-selectableaudiocapturepreview''.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== What just happened? ===&lt;br /&gt;
&lt;br /&gt;
The key to switching capture devices is a single call, &amp;lt;tt&amp;gt;SGSoundChannel.setDevice()&amp;lt;/tt&amp;gt; , which lets you change device mid-grab, without pausing or doing other reinitializations. It takes a device by name, the same name that was retrieved by walking through the &amp;lt;tt&amp;gt;SGDeviceList&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== What about... ===&lt;br /&gt;
&lt;br /&gt;
...the &amp;quot;0&amp;quot; parameter on &amp;lt;tt&amp;gt;getDeviceList( )&amp;lt;/tt&amp;gt; ? This method takes flags, only one of which is even relevant to QTJ.&lt;br /&gt;
&lt;br /&gt;
Actually, it's easier to explain by starting further down, with the test for whether to add a device to the &amp;lt;tt&amp;gt;Choice&amp;lt;/tt&amp;gt;. The &amp;lt;tt&amp;gt;SGDeviceName&amp;lt;/tt&amp;gt; used to identify the capture devices wraps not just a name string, but also an &amp;lt;tt&amp;gt;int&amp;lt;/tt&amp;gt; with some flag values. &amp;lt;tt&amp;gt;sgDeviceNameFlagDeviceUnavailable&amp;lt;/tt&amp;gt; is the only publicly documented flag. As seen in this example, to test for whether such a flag is set, you &amp;lt;tt&amp;gt;AND&amp;lt;/tt&amp;gt; the value with the flag you're interested in and check whether the result is nonzero. If so, it means that bit is set. So, in this case, if the value is 0, the device is available (literally, it's &amp;quot;not unavailable&amp;quot;), so it's OK to let the user select it.&lt;br /&gt;
&lt;br /&gt;
If we were to return to the &amp;lt;tt&amp;gt;getDeviceList( )&amp;lt;/tt&amp;gt;, the only flag available would be &amp;lt;tt&amp;gt;sgDeviceListDontCheckAvailability&amp;lt;/tt&amp;gt;, which skips the device availability check, meaning that flag in &amp;lt;tt&amp;gt;SGDeviceName&amp;lt;/tt&amp;gt; would never be set, and thus the device would never be reported as unavailable. That's clearly undesirable behavior here—you don't want to give the user an option that's only going to throw an exception when she chooses it.&lt;br /&gt;
&lt;br /&gt;
== Capturing Audio to Disk ==&lt;br /&gt;
&lt;br /&gt;
Typically, you don't just capture media and immediately dispose of it—you want to save the media to disk as you capture so that you can use it later. Fortunately, the &amp;lt;tt&amp;gt;SequenceGrabber&amp;lt;/tt&amp;gt; makes this pretty easy.&lt;br /&gt;
&lt;br /&gt;
=== How do I do that? ===&lt;br /&gt;
&lt;br /&gt;
Adding to the previous labs' code, the calls to set up the &amp;lt;tt&amp;gt;SequenceGrabber&amp;lt;/tt&amp;gt; need to be changed to prepare for grabbing to disk. Specifically, the &amp;lt;tt&amp;gt;SGSoundChannel&amp;lt;/tt&amp;gt; 's &amp;lt;tt&amp;gt;setUsage()&amp;lt;/tt&amp;gt; call gets a flag to indicate that it will be writing the captured audio to disk:&lt;br /&gt;
&lt;br /&gt;
 soundChannel.setUsage (StdQTConstants.seqGrabPreview |&lt;br /&gt;
                      StdQTConstants.seqGrabRecord);&lt;br /&gt;
&lt;br /&gt;
Next, add a call to give the user an opportunity to configure the audio capture:&lt;br /&gt;
&lt;br /&gt;
 soundChannel.settingsDialog( );&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;warning&amp;quot;&amp;gt;&lt;br /&gt;
'''Warning'''&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;settingsDialog( )&amp;lt;/tt&amp;gt; call will crash Java 1.4.2 on Mac OS X if called from the AWT event-dispatch thread. Yes, it's a bug. To work around this until the bug is fixed, you can stash the call in another thread and block on it. For instance, in this example you could replace the &amp;lt;tt&amp;gt;settingsDialog( )&amp;lt;/tt&amp;gt; call with the following:&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 final SGSoundChannel sc = soundChannel;&lt;br /&gt;
 Thread t = new Thread( ) {&lt;br /&gt;
 public void run( ) {&lt;br /&gt;
 try {&lt;br /&gt;
 sc.settingsDialog( );&lt;br /&gt;
 } catch (QTException qte) {&lt;br /&gt;
 qte.printStackTrace( );&lt;br /&gt;
 }&lt;br /&gt;
 }&lt;br /&gt;
 };&lt;br /&gt;
 t.start( );&lt;br /&gt;
 while (t.isAlive( ))&lt;br /&gt;
 Thread.yield( );&lt;br /&gt;
&lt;br /&gt;
After starting the preview, tell the &amp;lt;tt&amp;gt;SequenceGrabber&amp;lt;/tt&amp;gt; where it should save the captured audio:&lt;br /&gt;
&lt;br /&gt;
 // create output file&lt;br /&gt;
 grabFile = new QTFile (new java.io.File (&amp;quot;audiograb.mov&amp;quot;));&lt;br /&gt;
 if (grabFile.exists( ))&lt;br /&gt;
   grabFile.delete( );&lt;br /&gt;
 grabber.setDataOutput(grabFile,&lt;br /&gt;
                     StdQTConstants.seqGrabToDisk &lt;br /&gt;
                     //seqGrabDontAddMovieResource);&lt;br /&gt;
                     );&lt;br /&gt;
&lt;br /&gt;
Finally, start recording to this file with &amp;lt;tt&amp;gt;startRecord()&amp;lt;/tt&amp;gt; :&lt;br /&gt;
&lt;br /&gt;
 grabber.startRecord( );&lt;br /&gt;
&lt;br /&gt;
The last step is to provide a Stop button because the data is written to disk only when the &amp;lt;tt&amp;gt;SequenceGrabber.stop()&amp;lt;/tt&amp;gt; method is called. This Stop button is added near the bottom of the constructor, before the &amp;lt;tt&amp;gt;SequenceGrabber&amp;lt;/tt&amp;gt; is set up:&lt;br /&gt;
&lt;br /&gt;
 stopButton = new Button (&amp;quot;Stop&amp;quot;);&lt;br /&gt;
 stopButton.addActionListener (this);&lt;br /&gt;
 add (stopButton);&lt;br /&gt;
&lt;br /&gt;
The button requires a new &amp;lt;tt&amp;gt;ActionEventListener&amp;lt;/tt&amp;gt; to make the &amp;lt;tt&amp;gt;SequenceGrabber.stop( )&amp;lt;/tt&amp;gt; call and close down the sample program:&lt;br /&gt;
&lt;br /&gt;
 public void actionPerformed (ActionEvent e) {&lt;br /&gt;
   if (e.getSource( ) =  = stopButton) {&lt;br /&gt;
       System.out.println (&amp;quot;Stop grabbing&amp;quot;);&lt;br /&gt;
       try {&lt;br /&gt;
           if (grabber != null) {&lt;br /&gt;
               grabber.stop( );&lt;br /&gt;
           }&lt;br /&gt;
       } catch (QTException qte) {&lt;br /&gt;
           qte.printStackTrace( );&lt;br /&gt;
       } finally {&lt;br /&gt;
           System.exit (0);&lt;br /&gt;
       }&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;note&amp;quot;&amp;gt;&lt;br /&gt;
'''Note'''&lt;br /&gt;
&lt;br /&gt;
''Run this example with ant run-ch06-audiocapturetodisk''.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When this &amp;lt;tt&amp;gt;AudioCaptureToDisk&amp;lt;/tt&amp;gt; sample program runs, the user sees an audio settings dialog, as shown in [[QuickTime for Java: A Developer's Notebook/Capture#quicktimejvaadn-CHP-6-FIG-3|Figure 6-3]].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;quicktimejvaadn-CHP-6-FIG-3&amp;quot;&amp;gt;&lt;br /&gt;
'''Figure 6-3. Audio channel settings dialog'''&lt;br /&gt;
&lt;br /&gt;
[[Image:QuickTime for Java: A Developer's Notebook_I_6_tt91.png|Audio channel settings dialog]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
After OKing the settings dialog, the capture begins. When the user clicks Stop, the &amp;lt;tt&amp;gt;SequenceGrabber&amp;lt;/tt&amp;gt; writes and closes the ''audiograb.mov'' file and the program exits.&lt;br /&gt;
&lt;br /&gt;
=== What just happened? ===&lt;br /&gt;
&lt;br /&gt;
Requesting that the &amp;lt;tt&amp;gt;SequenceGrabber&amp;lt;/tt&amp;gt; save to disk requires just the few extra steps detailed earlier:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;ol&amp;gt;&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;div&amp;gt;Add &amp;lt;tt&amp;gt;seqGrabRecord&amp;lt;/tt&amp;gt; to the channel's &amp;lt;tt&amp;gt;setUsage( )&amp;lt;/tt&amp;gt; call.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;tip&amp;quot;&amp;gt;&lt;br /&gt;
'''Tip'''&lt;br /&gt;
&lt;br /&gt;
At this point, you optionally can call the channel's &amp;lt;tt&amp;gt;settingsDialog()&amp;lt;/tt&amp;gt; to give the user a chance to configure the capture.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&amp;lt;/div&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;div&amp;gt;Call &amp;lt;tt&amp;gt;setOutput( )&amp;lt;/tt&amp;gt; on the &amp;lt;tt&amp;gt;SequenceGrabber&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&amp;lt;/div&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;li&amp;gt;&amp;lt;div&amp;gt;Call &amp;lt;tt&amp;gt;SequenceGrabber.startRecord()&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&amp;lt;/div&amp;gt;&amp;lt;/li&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/ol&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Also, the &amp;lt;tt&amp;gt;SequenceGrabber&amp;lt;/tt&amp;gt; must be explicitly &amp;lt;tt&amp;gt;stop( )&amp;lt;/tt&amp;gt;ped to write the captured data to disk.&lt;br /&gt;
&lt;br /&gt;
=== What about... ===&lt;br /&gt;
&lt;br /&gt;
...the &amp;lt;tt&amp;gt;SequenceGrabber.prepare()&amp;lt;/tt&amp;gt; call? If the second argument is &amp;lt;tt&amp;gt;prepareForRecord&amp;lt;/tt&amp;gt;, why isn't that set to &amp;lt;tt&amp;gt;true&amp;lt;/tt&amp;gt; for this example? Well, inexplicably, when I did set it to &amp;lt;tt&amp;gt;true&amp;lt;/tt&amp;gt;, I started getting erroneous &amp;quot;dskFulErr&amp;quot; exceptions every time I &amp;lt;tt&amp;gt;idle()&amp;lt;/tt&amp;gt; d, even though I had 9 GB free. No, I don't know why—it's totally insane. But given the choice of what ''should'' work and what ''does'' work, I'll go with the latter.&lt;br /&gt;
&lt;br /&gt;
And what is the deal with the settings dialog? Could that have been used in the preview examples? Yes, absolutely. In fact, it's important to let the user adjust things like gain, or to specify a compressor before grabbing begins. But that's more important when you're actually grabbing to disk, so I held off introducing it until now.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;note&amp;quot;&amp;gt;&lt;br /&gt;
'''Note'''&lt;br /&gt;
&lt;br /&gt;
''Actually, it's usually best to capture uncompressed, so the CPU doesn't get bogged down with compression and possibly slow down the capture rate''.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Capturing Video to Disk ==&lt;br /&gt;
&lt;br /&gt;
Audio capture is nice, but if you bought this book because the sticky-note on the cover lists &amp;quot;capture&amp;quot; as one of the topics to be covered, you probably figured it meant video capture. Is there an iSight on the top of your monitor that wants some attention? OK, here's how to turn it on and grab some video.&lt;br /&gt;
&lt;br /&gt;
=== How do I do that? ===&lt;br /&gt;
&lt;br /&gt;
As with audio capture, the basics of setting up capture are:&lt;br /&gt;
&lt;br /&gt;
# Create a &amp;lt;tt&amp;gt;SequenceGrabber&amp;lt;/tt&amp;gt;.&lt;br /&gt;
# Create and configure (with &amp;lt;tt&amp;gt;setUsage( )&amp;lt;/tt&amp;gt; and the &amp;lt;tt&amp;gt;settingsDialog( )&amp;lt;/tt&amp;gt;) the channels you're interested in—in this case, an &amp;lt;tt&amp;gt;SGVideoChannel&amp;lt;/tt&amp;gt;.&lt;br /&gt;
# Call &amp;lt;tt&amp;gt;SequenceGrabber.setOutput( )&amp;lt;/tt&amp;gt; to indicate the file to capture to.&lt;br /&gt;
# Call &amp;lt;tt&amp;gt;SequenceGrabber.startRecord( )&amp;lt;/tt&amp;gt; to begin grabbing to disk.&lt;br /&gt;
# Finish up with &amp;lt;tt&amp;gt;SequenceGrabber.stop( )&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
There is, however, a ''big'' difference with video. With no on-screen preview component available in QTJ 6.1, you must indicate where the &amp;lt;tt&amp;gt;SequenceGrabber&amp;lt;/tt&amp;gt; can draw to. The workaround is to create an off-screen &amp;lt;tt&amp;gt;QDGraphics&amp;lt;/tt&amp;gt; and hand it to the &amp;lt;tt&amp;gt;SequenceGrabber&amp;lt;/tt&amp;gt; via the &amp;lt;tt&amp;gt;setGWorld( )&amp;lt;/tt&amp;gt; call.&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;VideoCaptureToDisk&amp;lt;/tt&amp;gt; program, presented in [[QuickTime for Java: A Developer's Notebook/Capture#quicktimejvaadn-CHP-6-EX-2|Example 6-2]], offers a bare-bones video capture to a file called ''videograb.mov''.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;note&amp;quot;&amp;gt;&lt;br /&gt;
'''Note'''&lt;br /&gt;
&lt;br /&gt;
''Run this example with ant run-ch06-videocapturetodisk''&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;quicktimejvaadn-CHP-6-EX-2&amp;quot;&amp;gt;&lt;br /&gt;
'''Example 6-2. Recording captured video to disk'''&lt;br /&gt;
&lt;br /&gt;
 package com.oreilly.qtjnotebook.ch06;&lt;br /&gt;
  &lt;br /&gt;
 import quicktime.*;&lt;br /&gt;
 import quicktime.io.*;&lt;br /&gt;
 import quicktime.std.*;&lt;br /&gt;
 import quicktime.std.sg.*;&lt;br /&gt;
 import quicktime.std.movies.*;&lt;br /&gt;
 import quicktime.std.image.*;&lt;br /&gt;
 import quicktime.qd.*;&lt;br /&gt;
 import quicktime.sound.*;&lt;br /&gt;
 import java.awt.*;&lt;br /&gt;
 import java.awt.event.*;&lt;br /&gt;
 import javax.swing.Timer;&lt;br /&gt;
 import com.oreilly.qtjnotebook.ch01.QTSessionCheck;&lt;br /&gt;
 public class VideoCaptureToDisk extends Frame &lt;br /&gt;
   implements ActionListener {&lt;br /&gt;
   SequenceGrabber grabber;&lt;br /&gt;
   SGVideoChannel videoChannel;&lt;br /&gt;
   QDGraphics gw;&lt;br /&gt;
   QDRect grabBounds;&lt;br /&gt;
   boolean grabbing;&lt;br /&gt;
   Button stopButton;&lt;br /&gt;
   QTFile grabFile;&lt;br /&gt;
   public VideoCaptureToDisk( ) throws QTException {&lt;br /&gt;
       super (&amp;quot;Video Capture&amp;quot;);&lt;br /&gt;
       QTSessionCheck.check( );&lt;br /&gt;
       setLayout (new GridLayout (2, 1));&lt;br /&gt;
       add (new Label (&amp;quot;Capturing video...&amp;quot;));&lt;br /&gt;
       stopButton = new Button (&amp;quot;Stop&amp;quot;);&lt;br /&gt;
       stopButton.addActionListener (this);&lt;br /&gt;
       add (stopButton);&lt;br /&gt;
       setUpVideoGrab( );&lt;br /&gt;
   }&lt;br /&gt;
   public void actionPerformed (ActionEvent e) {&lt;br /&gt;
       if (e.getSource( ) =  = stopButton) {&lt;br /&gt;
           System.out.println (&amp;quot;Stop grabbing&amp;quot;);&lt;br /&gt;
           try {&lt;br /&gt;
               grabbing = false;&lt;br /&gt;
               if (grabber != null) {&lt;br /&gt;
                   grabber.stop( );&lt;br /&gt;
               }&lt;br /&gt;
           } catch (Exception ex) {&lt;br /&gt;
               ex.printStackTrace( );&lt;br /&gt;
           } finally {&lt;br /&gt;
               System.exit (0);&lt;br /&gt;
           }&lt;br /&gt;
       }&lt;br /&gt;
   }&lt;br /&gt;
  &lt;br /&gt;
   protected void setUpVideoGrab( ) throws QTException {&lt;br /&gt;
       grabber = new SequenceGrabber( );&lt;br /&gt;
       System.out.println (&amp;quot;got grabber&amp;quot;);&lt;br /&gt;
       // force an offscreen gworld&lt;br /&gt;
       grabBounds = new QDRect (320, 240);&lt;br /&gt;
       gw = new QDGraphics (grabBounds);&lt;br /&gt;
       grabber.setGWorld (gw, null);&lt;br /&gt;
       // get videoChannel and set its bounds&lt;br /&gt;
       videoChannel = new SGVideoChannel (grabber);&lt;br /&gt;
       System.out.println (&amp;quot;Got SGVideoChannel&amp;quot;);&lt;br /&gt;
       videoChannel.setBounds (grabBounds);&lt;br /&gt;
       // get settings&lt;br /&gt;
       // yikes! this crashes java 1.4.2 on mac os x!&lt;br /&gt;
       videoChannel.settingsDialog( );&lt;br /&gt;
       // prepare and start previewing&lt;br /&gt;
       // note - second prepare arg should seemingly be false,&lt;br /&gt;
       // but if it is, you get erroneous dskFulErr's&lt;br /&gt;
       videoChannel.setUsage (StdQTConstants.seqGrabRecord);&lt;br /&gt;
       grabber.prepare(false, true);&lt;br /&gt;
       grabber.startPreview( );&lt;br /&gt;
       // create output file&lt;br /&gt;
       grabFile = new QTFile (new java.io.File (&amp;quot;videograb.mov&amp;quot;));&lt;br /&gt;
       grabber.setDataOutput(grabFile,&lt;br /&gt;
                             StdQTConstants.seqGrabToDisk &lt;br /&gt;
                             //seqGrabDontAddMovieResource);&lt;br /&gt;
                             );&lt;br /&gt;
       grabber.startRecord( );&lt;br /&gt;
       grabbing = true;&lt;br /&gt;
       // set up thread to idle&lt;br /&gt;
       ActionListener timerCallback =&lt;br /&gt;
           new ActionListener( ) {&lt;br /&gt;
               public void actionPerformed(ActionEvent e) {&lt;br /&gt;
                   if (grabbing) {&lt;br /&gt;
                       try {&lt;br /&gt;
                           grabber.idle( );&lt;br /&gt;
                           grabber.update(null);&lt;br /&gt;
                       } catch (QTException qte) {&lt;br /&gt;
                           qte.printStackTrace( );&lt;br /&gt;
                       }&lt;br /&gt;
                   }&lt;br /&gt;
               }&lt;br /&gt;
           };&lt;br /&gt;
       Timer timer = new Timer (50, timerCallback);&lt;br /&gt;
       timer.start( );&lt;br /&gt;
   }&lt;br /&gt;
   public static void main (String[  ] args) {&lt;br /&gt;
       try {&lt;br /&gt;
           Frame f = new VideoCaptureToDisk( );&lt;br /&gt;
           f.pack( );&lt;br /&gt;
           f.setVisible(true);&lt;br /&gt;
       } catch (QTException qte) {&lt;br /&gt;
           qte.printStackTrace( );&lt;br /&gt;
       }&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;note&amp;quot;&amp;gt;&lt;br /&gt;
'''Note'''&lt;br /&gt;
&lt;br /&gt;
''Run this example with ant run-ch06-videocapturetodisk''.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When it starts up, the program shows a settings dialog for your default camera, as seen in [[QuickTime for Java: A Developer's Notebook/Capture#quicktimejvaadn-CHP-6-FIG-4|Figure 6-4]]. The video settings dialog is even more important for users than the audio settings dialog, as the video dialog gives them a chance to aim the camera, check the lighting, adjust brightness and color, etc.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;quicktimejvaadn-CHP-6-FIG-4&amp;quot;&amp;gt;&lt;br /&gt;
'''Figure 6-4. Video channel settings dialog'''&lt;br /&gt;
&lt;br /&gt;
[[Image:QuickTime for Java: A Developer's Notebook_I_6_tt92.png|Video channel settings dialog]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;warning&amp;quot;&amp;gt;&lt;br /&gt;
'''Warning'''&lt;br /&gt;
&lt;br /&gt;
Just like its audio equivalent, calling &amp;lt;tt&amp;gt;SGVideoChannel.settingsDialog( )&amp;lt;/tt&amp;gt; will crash the virtual machine in Mac OS X Java 1.4.2 if called from the AWT event-dispatch thread. And just as before, you can work around this bug by firing off the &amp;lt;tt&amp;gt;settingsDialog( )&amp;lt;/tt&amp;gt; call in its own thread and blocking until the thread finishes. I've filed it as a bug, but feel free to file a duplicate to get Apple's attention.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Once you click the Stop button, the video is written to ''videograb.mov'' and the application terminates. You can view the captured movie in any QuickTime application—[[QuickTime for Java: A Developer's Notebook/Capture#quicktimejvaadn-CHP-6-FIG-5|Figure 6-5]] shows it in the &amp;lt;tt&amp;gt;BasicQTController&amp;lt;/tt&amp;gt; demo from [[QuickTime for Java: A Developer's Notebook/Playing Movies|Chapter 2]].&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;quicktimejvaadn-CHP-6-FIG-5&amp;quot;&amp;gt;&lt;br /&gt;
'''Figure 6-5. Captured video playing in a window'''&lt;br /&gt;
&lt;br /&gt;
[[Image:QuickTime for Java: A Developer's Notebook_I_6_tt93.png|Captured video playing in a window]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== What just happened? ===&lt;br /&gt;
&lt;br /&gt;
The critical step in doing video capture, at least until QuickTime adds on-screen preview, is to create an off-screen &amp;lt;tt&amp;gt;QDGraphics&amp;lt;/tt&amp;gt; and set that as the &amp;lt;tt&amp;gt;SequenceGrabber&amp;lt;/tt&amp;gt;'s &amp;lt;tt&amp;gt;GWorld&amp;lt;/tt&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
 // force an offscreen gworld&lt;br /&gt;
 grabBounds = new QDRect (320, 240);&lt;br /&gt;
 gw = new QDGraphics (grabBounds);&lt;br /&gt;
 grabber.setGWorld (gw, null);&lt;br /&gt;
&lt;br /&gt;
In previous versions of QTJ, this wasn't necessary because the on-screen preview provided a &amp;lt;tt&amp;gt;GWorld&amp;lt;/tt&amp;gt; that the grabber could use. With no on-screen preview currently available in QTJ, this is a handy technique.&lt;br /&gt;
&lt;br /&gt;
The next step is to create an &amp;lt;tt&amp;gt;SGVideoChannel&amp;lt;/tt&amp;gt; from the &amp;lt;tt&amp;gt;SequenceGrabber&amp;lt;/tt&amp;gt; and set its bounds. After optionally showing a settings dialog, set the usage (just &amp;lt;tt&amp;gt;seqGrabRecord&amp;lt;/tt&amp;gt; this time because there's no preview) and then call &amp;lt;tt&amp;gt;prepare(false, true)&amp;lt;/tt&amp;gt;, which prepares the &amp;lt;tt&amp;gt;SequenceGrabber&amp;lt;/tt&amp;gt; for recording but not for previewing.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;note&amp;quot;&amp;gt;&lt;br /&gt;
'''Note'''&lt;br /&gt;
&lt;br /&gt;
''This time, setting the second prepare( ) argument to true is the right thing to do''.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Just as with audio, the final steps are to call &amp;lt;tt&amp;gt;setDataOutput( )&amp;lt;/tt&amp;gt; on the &amp;lt;tt&amp;gt;SequenceGrabber&amp;lt;/tt&amp;gt;, followed by &amp;lt;tt&amp;gt;startRecord( )&amp;lt;/tt&amp;gt;. When &amp;lt;tt&amp;gt;SequenceGrabber.stop( )&amp;lt;/tt&amp;gt; is called, the file is written out and closed up.&lt;br /&gt;
&lt;br /&gt;
=== What about... ===&lt;br /&gt;
&lt;br /&gt;
...using this on Windows...it doesn't find my webcam! This example presupposes that a ''video digitizer component'' for your camera will be found, and a lot of video cameras don't ship with a Windows QuickTime &amp;quot;&amp;lt;tt&amp;gt;vdig&amp;lt;/tt&amp;gt;&amp;quot;, supporting only Microsoft's media APIs instead. However, there's hope: you can use SoftVDIG from Abstract Plane (''http://www.abstractplane.com.au''), which acts as a proxy to bring captured video from the Microsoft DirectShow world into QuickTime.&lt;br /&gt;
&lt;br /&gt;
== Capturing Audio and Video to the Same File ==&lt;br /&gt;
&lt;br /&gt;
So, it's possible to capture audio and video in isolation. With QuickTime's editing API, it would be possible to put them in the same movie by adding each as a separate track (see [[QuickTime for Java: A Developer's Notebook/Editing Movies|Chapter 3]]). But wouldn't it be nice to just capture both audio and video into the same file at once, presumably keeping them in sync along the way? Fortunately, &amp;lt;tt&amp;gt;SequenceGrabber&amp;lt;/tt&amp;gt; supports this, too.&lt;br /&gt;
&lt;br /&gt;
=== How do I do that? ===&lt;br /&gt;
&lt;br /&gt;
Starting with the previous lab's video-only example, you just need to add an &amp;lt;tt&amp;gt;SGSoundChannel&amp;lt;/tt&amp;gt; in the &amp;lt;tt&amp;gt;setUpVideoGrab( )&amp;lt;/tt&amp;gt; method:&lt;br /&gt;
&lt;br /&gt;
 soundChannel = new SGSoundChannel (grabber);&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;setUsage( )&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;prepare()&amp;lt;/tt&amp;gt; commands are identical to what was shown in the audio-only and video-only labs:&lt;br /&gt;
&lt;br /&gt;
 // prepare and start previewing&lt;br /&gt;
 videoChannel.setUsage (StdQTConstants.seqGrabRecord);&lt;br /&gt;
 soundChannel.setUsage (StdQTConstants.seqGrabPreview |&lt;br /&gt;
                      StdQTConstants.seqGrabRecord);&lt;br /&gt;
 soundChannel.setVolume (0.0f);&lt;br /&gt;
 grabber.prepare(false, true);&lt;br /&gt;
 grabber.startPreview( );&lt;br /&gt;
&lt;br /&gt;
Beyond that, everything is the same as in the video-only case. Because the &amp;lt;tt&amp;gt;setDataOutput()&amp;lt;/tt&amp;gt; call is made on the &amp;lt;tt&amp;gt;SequenceGrabber&amp;lt;/tt&amp;gt;—not just on an individual channel—the grabber writes data from all the channels it's capturing into the same file, called ''audiovideograb.mov'' in this case.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;note&amp;quot;&amp;gt;&lt;br /&gt;
'''Note'''&lt;br /&gt;
&lt;br /&gt;
''Run this example with ant run-ch06-audiovideocapturetodisk''.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== What just happened? ===&lt;br /&gt;
&lt;br /&gt;
For once, the &amp;lt;tt&amp;gt;SequenceGrabber&amp;lt;/tt&amp;gt; APIs behave pretty much as you might expect them to. With no obvious prohibition on creating both audio and video channels from the same &amp;lt;tt&amp;gt;SequenceGrabber&amp;lt;/tt&amp;gt;, and assigning the grabber's output to a file, the captured data from both channels goes into a single movie file.&lt;br /&gt;
&lt;br /&gt;
== Making a Motion Detector ==&lt;br /&gt;
&lt;br /&gt;
Capture isn't just about writing data to disk. You can grab images as they come in and analyze or manipulate them.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;tip&amp;quot;&amp;gt;&lt;br /&gt;
'''Tip'''&lt;br /&gt;
&lt;br /&gt;
A great example of &amp;quot;grabbing different&amp;quot; is Lisa Lippincott's ScrollPlate, a demo shown at ADHOC 2004. She used her iSight camera as a scroll wheel, by holding up a Styrofoam plate with either a large green arrow (for up) or a large red arrow (for down). Her code presumably grabbed from the camera, looked at the grabbed image for an abundance of green or red, and scrolled the top window in response.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This example offers a simple motion detector, which will display an alarm message if two subsequent grabs are markedly different. The idea is that if the camera is not moving, a significant difference between two subsequent grabs indicates that something in view of the camera has moved.&lt;br /&gt;
&lt;br /&gt;
=== How do I do that? ===&lt;br /&gt;
&lt;br /&gt;
In this case, what you want to do is to set up video-only capture, but instead of saving the data to disk, you do a little bit of image processing each time you &amp;lt;tt&amp;gt;idle()&amp;lt;/tt&amp;gt; . Specifically, there is a method in &amp;lt;tt&amp;gt;QTImage&amp;lt;/tt&amp;gt; called &amp;lt;tt&amp;gt;getSimilarity( )&amp;lt;/tt&amp;gt;, which compares two images (one as a &amp;lt;tt&amp;gt;QDGraphics&amp;lt;/tt&amp;gt; and the other as an &amp;lt;tt&amp;gt;EncodedImage&amp;lt;/tt&amp;gt;). Motion—objects entering, exiting, or significantly moving within the camera's field of vision—can be understood as a significant difference between two consecutive grabbed images.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;note&amp;quot;&amp;gt;&lt;br /&gt;
'''Note'''&lt;br /&gt;
&lt;br /&gt;
''See [[QuickTime for Java: A Developer's Notebook/Working with QuickDraw|Chapter 5]] for more on QTImage, QDGraphics, and EncodedImage''.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Unfortunately, this requires jumping through quite a bit of QuickDraw hoops once an image is grabbed from the camera. [[QuickTime for Java: A Developer's Notebook/Capture#quicktimejvaadn-CHP-6-EX-3|Example 6-3]] shows the &amp;lt;tt&amp;gt;SimpleMotionDetector&amp;lt;/tt&amp;gt; code.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;note&amp;quot;&amp;gt;&lt;br /&gt;
'''Note'''&lt;br /&gt;
&lt;br /&gt;
''Run this example with ant run-ch06-simplemotiondetector''.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;quicktimejvaadn-CHP-6-EX-3&amp;quot;&amp;gt;&lt;br /&gt;
'''Example 6-3. Detecting motion by comparing grabbed images'''&lt;br /&gt;
&lt;br /&gt;
 package com.oreilly.qtjnotebook.ch06;&lt;br /&gt;
  &lt;br /&gt;
 import quicktime.*;&lt;br /&gt;
 import quicktime.io.*;&lt;br /&gt;
 import quicktime.std.*;&lt;br /&gt;
 import quicktime.std.sg.*;&lt;br /&gt;
 import quicktime.std.movies.*;&lt;br /&gt;
 import quicktime.std.movies.media.*;&lt;br /&gt;
 import quicktime.std.image.*;&lt;br /&gt;
 import quicktime.qd.*;&lt;br /&gt;
 import quicktime.sound.*;&lt;br /&gt;
 import quicktime.app.view.*;&lt;br /&gt;
 import quicktime.util.*;&lt;br /&gt;
 import java.awt.*;&lt;br /&gt;
 import java.awt.event.*;&lt;br /&gt;
 import javax.swing.Timer;&lt;br /&gt;
 import java.text.*;&lt;br /&gt;
 import com.oreilly.qtjnotebook.ch01.QTSessionCheck;&lt;br /&gt;
 public class SimpleMotionDetector extends Frame &lt;br /&gt;
   implements ActionListener {&lt;br /&gt;
   SequenceGrabber grabber;&lt;br /&gt;
   SGVideoChannel videoChannel;&lt;br /&gt;
   QDGraphics gw;&lt;br /&gt;
   QDRect grabBounds;&lt;br /&gt;
   boolean grabbing;&lt;br /&gt;
   Button stopButton;&lt;br /&gt;
   Pict grabPict;&lt;br /&gt;
   byte[  ] importPictBytes;&lt;br /&gt;
   Component importerComponent;&lt;br /&gt;
   Label motionLabel;&lt;br /&gt;
   GraphicsImporter importer;&lt;br /&gt;
   RawEncodedImage lastImage;&lt;br /&gt;
   ImageDescription lastImageDescription;&lt;br /&gt;
   byte[  ] lastImageBytes;&lt;br /&gt;
   QDGraphics newImageGW;&lt;br /&gt;
   int thumbcount = 0;&lt;br /&gt;
   // lesser numbers are more different (0 =  = totally different)&lt;br /&gt;
   // public static float trigger = 0.0002f;&lt;br /&gt;
   public static float trigger = 0.002f;&lt;br /&gt;
  &lt;br /&gt;
   public SimpleMotionDetector( ) throws QTException {&lt;br /&gt;
       super (&amp;quot;Simple Motion Detector&amp;quot;);&lt;br /&gt;
       QTSessionCheck.check( );&lt;br /&gt;
       setLayout (new BorderLayout( ));&lt;br /&gt;
       motionLabel = new Label ( );&lt;br /&gt;
       motionLabel.setForeground (Color.red);&lt;br /&gt;
       add (motionLabel, BorderLayout.NORTH);&lt;br /&gt;
       stopButton = new Button (&amp;quot;Stop&amp;quot;);&lt;br /&gt;
       stopButton.addActionListener (this);&lt;br /&gt;
       add (stopButton, BorderLayout.SOUTH);&lt;br /&gt;
       importer = new GraphicsImporter (StdQTConstants.kQTFileTypePicture);&lt;br /&gt;
       importerComponent =&lt;br /&gt;
           QTFactory.makeQTComponent(importer).asComponent( );&lt;br /&gt;
       add (importerComponent, BorderLayout.CENTER);&lt;br /&gt;
       setUpVideoGrab( );&lt;br /&gt;
   }&lt;br /&gt;
   public void actionPerformed (ActionEvent e) {&lt;br /&gt;
       if (e.getSource( ) =  = stopButton) {&lt;br /&gt;
           System.out.println (&amp;quot;Stop grabbing&amp;quot;);&lt;br /&gt;
           try {&lt;br /&gt;
               grabbing = false;&lt;br /&gt;
               if (grabber != null) {&lt;br /&gt;
                   grabber.stop( );&lt;br /&gt;
               }&lt;br /&gt;
           } catch (Exception ex) {&lt;br /&gt;
               ex.printStackTrace( );&lt;br /&gt;
           } finally {&lt;br /&gt;
               System.exit (0);&lt;br /&gt;
           }&lt;br /&gt;
       }&lt;br /&gt;
   }&lt;br /&gt;
   protected void setUpVideoGrab( ) throws QTException {&lt;br /&gt;
       grabber = new SequenceGrabber( );&lt;br /&gt;
       System.out.println (&amp;quot;got grabber&amp;quot;);&lt;br /&gt;
       // force an offscreen gworld&lt;br /&gt;
       grabBounds = new QDRect (320, 240);&lt;br /&gt;
       gw = new QDGraphics (grabBounds);&lt;br /&gt;
       grabber.setGWorld (gw, null);&lt;br /&gt;
       // get videoChannel and set its bounds&lt;br /&gt;
       videoChannel = new SGVideoChannel (grabber);&lt;br /&gt;
       System.out.println (&amp;quot;Got SGVideoChannel&amp;quot;);&lt;br /&gt;
       videoChannel.setBounds (grabBounds);&lt;br /&gt;
       // get settings&lt;br /&gt;
       // yikes! this crashes java 1.4.2 on mac os x!&lt;br /&gt;
       // videoChannel.settingsDialog( );&lt;br /&gt;
       // prepare and start previewing&lt;br /&gt;
       videoChannel.setUsage (StdQTConstants.seqGrabPreview);&lt;br /&gt;
       grabber.prepare(true, false);&lt;br /&gt;
       grabber.startPreview( );&lt;br /&gt;
       // get first grab, so we're ready&lt;br /&gt;
       // to calc diff's and draw component&lt;br /&gt;
       scanForDifference( );&lt;br /&gt;
       updateImportedPict( );&lt;br /&gt;
       grabbing = true;&lt;br /&gt;
       // set up thread to idle&lt;br /&gt;
       ActionListener timerCallback =&lt;br /&gt;
           new ActionListener( ) {&lt;br /&gt;
               public void actionPerformed(ActionEvent e) {&lt;br /&gt;
                   if (grabbing) {&lt;br /&gt;
                       try {&lt;br /&gt;
                           grabber.idle( );&lt;br /&gt;
                           grabber.update(null);&lt;br /&gt;
                           scanForDifference( );&lt;br /&gt;
                           updateImportedPict( );&lt;br /&gt;
                       } catch (QTException qte) {&lt;br /&gt;
                           qte.printStackTrace( );&lt;br /&gt;
                       }&lt;br /&gt;
                   }&lt;br /&gt;
               }&lt;br /&gt;
           };&lt;br /&gt;
       Timer timer = new Timer (2000, timerCallback);&lt;br /&gt;
       timer.start( );&lt;br /&gt;
   }&lt;br /&gt;
   protected void scanForDifference( ) throws QTException {&lt;br /&gt;
       // this seems like overkill, but the GW we give&lt;br /&gt;
       // the grabber doesn't get updated.  Picts returned&lt;br /&gt;
       // from grabber are different each time, so use 'em&lt;br /&gt;
       if (newImageGW =  = null)&lt;br /&gt;
           newImageGW = new QDGraphics (grabBounds);&lt;br /&gt;
       grabPict = grabber.grabPict (grabBounds, 0, 0);&lt;br /&gt;
       grabPict.draw (newImageGW, grabBounds);&lt;br /&gt;
       if (lastImage != null) {&lt;br /&gt;
           // compare to last image&lt;br /&gt;
           float similarity = QTImage.getSimilarity (newImageGW,&lt;br /&gt;
                                                     grabBounds,&lt;br /&gt;
                                                     lastImageDescription,&lt;br /&gt;
                                                     lastImage);&lt;br /&gt;
           System.out.println (&amp;quot;similarity =  = &amp;quot; +&lt;br /&gt;
                               formatter.format(similarity));&lt;br /&gt;
           if (similarity &amp;lt; trigger) {&lt;br /&gt;
               System.out.println (&amp;quot;*** Motion detect ***&amp;quot;);&lt;br /&gt;
               motionLabel.setText (&amp;quot;motion detect&amp;quot;);&lt;br /&gt;
           } else {&lt;br /&gt;
               motionLabel.setText (&amp;quot;&amp;quot;);&lt;br /&gt;
           }&lt;br /&gt;
       }&lt;br /&gt;
       // create a new lastImage from grabber GWorld&lt;br /&gt;
       int bufSize =&lt;br /&gt;
           QTImage.getMaxCompressionSize (newImageGW,&lt;br /&gt;
                                          newImageGW.getBounds( ),&lt;br /&gt;
                                          0,&lt;br /&gt;
                                          StdQTConstants.codecNormalQuality,&lt;br /&gt;
                                          StdQTConstants.kRawCodecType,&lt;br /&gt;
                                          CodecComponent.anyCodec);&lt;br /&gt;
       // make new lastImage&lt;br /&gt;
       lastImageBytes = new byte[bufSize];&lt;br /&gt;
       lastImage = new RawEncodedImage (lastImageBytes);&lt;br /&gt;
       lastImageDescription =&lt;br /&gt;
           QTImage.compress(newImageGW,&lt;br /&gt;
                            newImageGW.getBounds( ),&lt;br /&gt;
                            StdQTConstants.codecNormalQuality,&lt;br /&gt;
                            StdQTConstants.kRawCodecType,&lt;br /&gt;
                            lastImage);&lt;br /&gt;
  &lt;br /&gt;
   protected void updateImportedPict( ) throws QTException {&lt;br /&gt;
       importPictBytes = new byte [grabPict.getSize( ) + 512];&lt;br /&gt;
       grabPict.copyToArray (0,&lt;br /&gt;
                             importPictBytes,&lt;br /&gt;
                             512,&lt;br /&gt;
                             importPictBytes.length - 512);&lt;br /&gt;
       Pict wrapperPict = new Pict (importPictBytes);&lt;br /&gt;
       DataRef ref = new DataRef (wrapperPict,&lt;br /&gt;
                                  StdQTConstants.kDataRefQTFileTypeTag,&lt;br /&gt;
                                  &amp;quot;PICT&amp;quot;);&lt;br /&gt;
       importer.setDataReference (ref);&lt;br /&gt;
       importer.draw( );&lt;br /&gt;
       if (importerComponent != null)&lt;br /&gt;
           importerComponent.repaint( );&lt;br /&gt;
       // wrapperPict.disposeQTObject( );&lt;br /&gt;
   }&lt;br /&gt;
  &lt;br /&gt;
   public static void main (String[  ] args) {&lt;br /&gt;
       try {&lt;br /&gt;
           Frame f = new SimpleMotionDetector( );&lt;br /&gt;
           f.pack( );&lt;br /&gt;
           f.setVisible(true);&lt;br /&gt;
       } catch (QTException qte) {&lt;br /&gt;
           qte.printStackTrace( );&lt;br /&gt;
       }&lt;br /&gt;
   }&lt;br /&gt;
 }&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
When running, if two frames differ by more than a specified amount, the label &amp;quot;motion detect&amp;quot; will appear at the top of the window. [[QuickTime for Java: A Developer's Notebook/Capture#quicktimejvaadn-CHP-6-FIG-6|Figure 6-6]] shows the running application.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;quicktimejvaadn-CHP-6-FIG-6&amp;quot;&amp;gt;&lt;br /&gt;
'''Figure 6-6. Video motion detector window'''&lt;br /&gt;
&lt;br /&gt;
[[Image:QuickTime for Java: A Developer's Notebook_I_6_tt97.png|Video motion detector window]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== What just happened? ===&lt;br /&gt;
&lt;br /&gt;
This is a huge example, but much of it draws on the video-grabbing techniques of the previous two labs. &amp;lt;tt&amp;gt;setUpVideoGrab()&amp;lt;/tt&amp;gt; sets up the &amp;lt;tt&amp;gt;SequenceGrabber&amp;lt;/tt&amp;gt; for grabbing video, but in this case, it doesn't need to save to disk, so the &amp;lt;tt&amp;gt;setUsage()&amp;lt;/tt&amp;gt; argument is &amp;lt;tt&amp;gt;seqGrabPreview&amp;lt;/tt&amp;gt;, and the arguments to &amp;lt;tt&amp;gt;prepare( )&amp;lt;/tt&amp;gt; are &amp;lt;tt&amp;gt;true&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;false&amp;lt;/tt&amp;gt; (for preview and record, respectively). A Swing &amp;lt;tt&amp;gt;Timer&amp;lt;/tt&amp;gt; calls back every two seconds—the long delay is intentional, so the potential for change between grabbed frames is greater—and calls the &amp;lt;tt&amp;gt;SequenceGrabber&amp;lt;/tt&amp;gt; &amp;lt;tt&amp;gt;idle( )&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;update( )&amp;lt;/tt&amp;gt; methods, followed by calls to the brains of this example: &amp;lt;tt&amp;gt;scanForDifference()&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;updatePict( )&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;scanForDifference( )&amp;lt;/tt&amp;gt; evaluates the difference between the current frame and the last one. It does this by grabbing a &amp;lt;tt&amp;gt;Pict&amp;lt;/tt&amp;gt; from the &amp;lt;tt&amp;gt;SequenceGrabber&amp;lt;/tt&amp;gt; and drawing it into a &amp;lt;tt&amp;gt;QDGraphics&amp;lt;/tt&amp;gt; (also known as a &amp;lt;tt&amp;gt;GWorld&amp;lt;/tt&amp;gt;). It compares this &amp;lt;tt&amp;gt;GWorld&amp;lt;/tt&amp;gt; to an &amp;lt;tt&amp;gt;EncodedImage&amp;lt;/tt&amp;gt; of the last grab, via the &amp;lt;tt&amp;gt;QTImage.getSimilarity()&amp;lt;/tt&amp;gt; method. This method returns a &amp;lt;tt&amp;gt;float&amp;lt;/tt&amp;gt; that expresses the similarity of the two grabbed images, where &amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt; means the images are totally different and &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; means they're identical. At the end of this method, &amp;lt;tt&amp;gt;QTImage.compress( )&amp;lt;/tt&amp;gt; is used to compress the grabbed &amp;lt;tt&amp;gt;GWorld&amp;lt;/tt&amp;gt; into a new &amp;lt;tt&amp;gt;EncodedImage&amp;lt;/tt&amp;gt; for use on the next call to &amp;lt;tt&amp;gt;scanForDifference( )&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;note&amp;quot;&amp;gt;&lt;br /&gt;
'''Note'''&lt;br /&gt;
&lt;br /&gt;
''It might be better to call scanFor-Difference( ) on another thread, so the image analysis doesn't block the repeated calls to SequenceGrabber.idle( )''.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;tt&amp;gt;updatePict( )&amp;lt;/tt&amp;gt; updates a &amp;lt;tt&amp;gt;GraphicsImporter&amp;lt;/tt&amp;gt; that is used to provide the preview image in the middle of the window. This uses a &amp;lt;tt&amp;gt;Pict&amp;lt;/tt&amp;gt;-to-&amp;lt;tt&amp;gt;GraphicsImporter&amp;lt;/tt&amp;gt; trick that was introduced in [[QuickTime for Java: A Developer's Notebook/Working with QuickDraw|Chapter 5]]s [[QuickTime for Java: A Developer's Notebook/Working with QuickDraw#A Better Movie-to-Java Image Converter|Section 5.4]] lab. In this case, it's used not to get a Java AWT &amp;lt;tt&amp;gt;Image&amp;lt;/tt&amp;gt;, but to get new pixels into a &amp;lt;tt&amp;gt;GraphicsImporter&amp;lt;/tt&amp;gt;, which is wired up to a &amp;lt;tt&amp;gt;QTComponent&amp;lt;/tt&amp;gt; for on-screen preview.&lt;br /&gt;
&lt;br /&gt;
=== What about... ===&lt;br /&gt;
&lt;br /&gt;
...the ideal value for triggering a difference? It probably depends on lighting, your camera, and other factors. In a professional application, you'd want to give the user a slider or some similar means of configuring the sensitivity of the detection.&lt;br /&gt;
&lt;br /&gt;
Also, there seems to be a lot of inefficient code here, particularly with drawing into the &amp;lt;tt&amp;gt;newImageGW&amp;lt;/tt&amp;gt;. Why is that necessary when the &amp;lt;tt&amp;gt;Grabber&amp;lt;/tt&amp;gt; was initially set up with a brand-new off-screen &amp;lt;tt&amp;gt;QDGraphics&amp;lt;/tt&amp;gt; /&amp;lt;tt&amp;gt;GWorld&amp;lt;/tt&amp;gt;? This, admittedly, is weird. When I was debugging, I found that the &amp;lt;tt&amp;gt;GWorld&amp;lt;/tt&amp;gt; used to set up the &amp;lt;tt&amp;gt;Grabber&amp;lt;/tt&amp;gt; is drawn to ''once'' and never again. On the other hand, the &amp;lt;tt&amp;gt;Pict&amp;lt;/tt&amp;gt; generated from &amp;lt;tt&amp;gt;SequenceGrabber.grabPict( )&amp;lt;/tt&amp;gt; is always fresh, so that's what's used for testing similarity. However, to apply the &amp;lt;tt&amp;gt;getSimilarity( )&amp;lt;/tt&amp;gt; method, you need to have a &amp;lt;tt&amp;gt;GWorld&amp;lt;/tt&amp;gt;, so you &amp;lt;tt&amp;gt;Pict.draw( )&amp;lt;/tt&amp;gt; the pixels from the &amp;lt;tt&amp;gt;Pict&amp;lt;/tt&amp;gt; into the &amp;lt;tt&amp;gt;GWorld&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
Come to think of it, with this application updating the component with a new grab every couple of seconds, isn't that effectively an on-screen preview? Yes, it is, in an extraordinarily roundabout way. You could take out the motion-detecting stuff and make a preview component by just grabbing a &amp;lt;tt&amp;gt;Pict&amp;lt;/tt&amp;gt; each time, making a new &amp;lt;tt&amp;gt;Pict&amp;lt;/tt&amp;gt; with a 512-byte header, setting the &amp;lt;tt&amp;gt;GraphicsImporter&amp;lt;/tt&amp;gt; to read that, and calling &amp;lt;tt&amp;gt;GraphicsImporter.draw( )&amp;lt;/tt&amp;gt; to draw into its on-screen component. I didn't split that out as its own lab because the performance is pathologically bad (one frame per second—at best), and because it's an awkward workaround in lieu of a better way of getting a component from a &amp;lt;tt&amp;gt;SequenceGrabber&amp;lt;/tt&amp;gt;. Presumably, someday there will be a proper call to get a &amp;lt;tt&amp;gt;QTComponent&amp;lt;/tt&amp;gt; from a &amp;lt;tt&amp;gt;SequenceGrabber&amp;lt;/tt&amp;gt;—maybe another overload of &amp;lt;tt&amp;gt;QTFactory.makeQTComponent( )&amp;lt;/tt&amp;gt;—and kludgery like this won't be necessary.&lt;/div&gt;</summary>
		<author><name>Docbook2Wiki</name></author>	</entry>

	</feed>