1page.title=TV Audio
2@jd:body
3
4<!--
5    Copyright 2015 The Android Open Source Project
6
7    Licensed under the Apache License, Version 2.0 (the "License");
8    you may not use this file except in compliance with the License.
9    You may obtain a copy of the License at
10
11        http://www.apache.org/licenses/LICENSE-2.0
12
13    Unless required by applicable law or agreed to in writing, software
14    distributed under the License is distributed on an "AS IS" BASIS,
15    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16    See the License for the specific language governing permissions and
17    limitations under the License.
18-->
19<div id="qv-wrapper">
20  <div id="qv">
21    <h2>In this document</h2>
22    <ol id="auto-toc">
23    </ol>
24  </div>
25</div>
26
27<p>The TV Input Framework (TIF) manager works with the audio routing API to support flexible audio
28path changes. When a System on Chip (SoC) implements the TV hardware abstraction layer (HAL), each
29TV input (HDMI IN, Tuner, etc.) provides <code>TvInputHardwareInfo</code> that specifies AudioPort information for audio type and address.</p>
30
31<ul>
32<li><b>Physical</b> audio input/output devices have a corresponding AudioPort.</li>
33<li><b>Software</b> audio output/input streams are represented as AudioMixPort (child class of
34AudioPort).</li>
35</ul>
36
37<p>The TIF then uses AudioPort information for the audio routing API.</p>
38
39<p><img src="images/ape_audio_tv_tif.png" alt="Android TV Input Framework (TIF)" />
40<p class="img-caption"><strong>Figure 1.</strong> TV Input Framework (TIF)</p>
41
42<h2 id="Requirements">Requirements</h2>
43
44<p>A SoC must implement the audio HAL with the following audio routing API support:</p>
45
46<table>
47<tbody>
48<tr>
49<th>Audio Ports</th>
50<td>
51<ul>
52<li>TV Audio Input has a corresponding audio source port implementation.</li>
53<li>TV Audio Output has a corresponding audio sink port implementation.</li>
54<li>Can create audio patch between any TV input audio port and any TV output audio port.</li>
55</ul>
56</td>
57</tr>
58<tr>
59<th>Default Input</th>
60<td>AudioRecord (created with DEFAULT input source) must seize <i>virtual null input source</i> for
61AUDIO_DEVICE_IN_DEFAULT acquisition on Android TV.</td>
62</tr>
63<tr>
64<th>Device Loopback</th>
65<td>Requires supporting an AUDIO_DEVICE_IN_LOOPBACK input that is a complete mix of all audio output
66of all the TV output (11Khz, 16bit mono or 48Khz, 16bit mono). Used only for audio capture.
67</td>
68</tr>
69</tbody>
70</table>
71
72
73<h2 id="Audio Devices">TV audio devices</h2>
74
75<p>Android supports the following audio devices for TV audio input/output.</p>
76
77<h4>system/media/audio/include/system/audio.h</h4>
78
79<p class="note"><strong>Note:</strong> In Android 5.1 and earlier, the path to
80this file is: <code>system/core/include/system/audio.h</code></p>
81
82<pre>
83/* output devices */
84AUDIO_DEVICE_OUT_AUX_DIGITAL  = 0x400,
85AUDIO_DEVICE_OUT_HDMI   = AUDIO_DEVICE_OUT_AUX_DIGITAL,
86/* HDMI Audio Return Channel */
87AUDIO_DEVICE_OUT_HDMI_ARC   = 0x40000,
88/* S/PDIF out */
89AUDIO_DEVICE_OUT_SPDIF    = 0x80000,
90/* input devices */
91AUDIO_DEVICE_IN_AUX_DIGITAL   = AUDIO_DEVICE_BIT_IN | 0x20,
92AUDIO_DEVICE_IN_HDMI      = AUDIO_DEVICE_IN_AUX_DIGITAL,
93/* TV tuner input */
94AUDIO_DEVICE_IN_TV_TUNER    = AUDIO_DEVICE_BIT_IN | 0x4000,
95/* S/PDIF in */
96AUDIO_DEVICE_IN_SPDIF   = AUDIO_DEVICE_BIT_IN | 0x10000,
97AUDIO_DEVICE_IN_LOOPBACK    = AUDIO_DEVICE_BIT_IN | 0x40000,
98</pre>
99
100
101<h2 id="HAL extension">Audio HAL extension</h2>
102
103<p>The Audio HAL extension for the audio routing API is defined by following:</p>
104
105<h4>system/media/audio/include/system/audio.h</h4>
106
107<p class="note"><strong>Note:</strong> In Android 5.1 and earlier, the path to
108this file is: <code>system/core/include/system/audio.h</code></p>
109
110<pre>
111/* audio port configuration structure used to specify a particular configuration of an audio port */
112struct audio_port_config {
113    audio_port_handle_t      id;           /* port unique ID */
114    audio_port_role_t        role;         /* sink or source */
115    audio_port_type_t        type;         /* device, mix ... */
116    unsigned int             config_mask;  /* e.g AUDIO_PORT_CONFIG_ALL */
117    unsigned int             sample_rate;  /* sampling rate in Hz */
118    audio_channel_mask_t     channel_mask; /* channel mask if applicable */
119    audio_format_t           format;       /* format if applicable */
120    struct audio_gain_config gain;         /* gain to apply if applicable */
121    union {
122        struct audio_port_config_device_ext  device;  /* device specific info */
123        struct audio_port_config_mix_ext     mix;     /* mix specific info */
124        struct audio_port_config_session_ext session; /* session specific info */
125    } ext;
126};
127struct audio_port {
128    audio_port_handle_t      id;                /* port unique ID */
129    audio_port_role_t        role;              /* sink or source */
130    audio_port_type_t        type;              /* device, mix ... */
131    unsigned int             num_sample_rates;  /* number of sampling rates in following array */
132    unsigned int             sample_rates[AUDIO_PORT_MAX_SAMPLING_RATES];
133    unsigned int             num_channel_masks; /* number of channel masks in following array */
134    audio_channel_mask_t     channel_masks[AUDIO_PORT_MAX_CHANNEL_MASKS];
135    unsigned int             num_formats;       /* number of formats in following array */
136    audio_format_t           formats[AUDIO_PORT_MAX_FORMATS];
137    unsigned int             num_gains;         /* number of gains in following array */
138    struct audio_gain        gains[AUDIO_PORT_MAX_GAINS];
139    struct audio_port_config active_config;     /* current audio port configuration */
140    union {
141        struct audio_port_device_ext  device;
142        struct audio_port_mix_ext     mix;
143        struct audio_port_session_ext session;
144    } ext;
145};
146</pre>
147
148<h4>hardware/libhardware/include/hardware/audio.h</h4>
149
150<pre>
151struct audio_hw_device {
152  :
153    /**
154     * Routing control
155     */
156
157    /* Creates an audio patch between several source and sink ports.
158     * The handle is allocated by the HAL and should be unique for this
159     * audio HAL module. */
160    int (*create_audio_patch)(struct audio_hw_device *dev,
161                               unsigned int num_sources,
162                               const struct audio_port_config *sources,
163                               unsigned int num_sinks,
164                               const struct audio_port_config *sinks,
165                               audio_patch_handle_t *handle);
166
167    /* Release an audio patch */
168    int (*release_audio_patch)(struct audio_hw_device *dev,
169                               audio_patch_handle_t handle);
170
171    /* Fills the list of supported attributes for a given audio port.
172     * As input, "port" contains the information (type, role, address etc...)
173     * needed by the HAL to identify the port.
174     * As output, "port" contains possible attributes (sampling rates, formats,
175     * channel masks, gain controllers...) for this port.
176     */
177    int (*get_audio_port)(struct audio_hw_device *dev,
178                          struct audio_port *port);
179
180    /* Set audio port configuration */
181    int (*set_audio_port_config)(struct audio_hw_device *dev,
182                         const struct audio_port_config *config);
183</pre>
184
185<h2 id="Testing">Testing DEVICE_IN_LOOPBACK</h2>
186
187<p>To test DEVICE_IN_LOOPBACK for TV monitoring, use the following testing code. After running the
188test, the captured audio saves to <code>/sdcard/record_loopback.raw</code>, where you can listen to
189it using <code>ffmeg</code>.</p>
190
191<pre>
192&lt;uses-permission android:name="android.permission.MODIFY_AUDIO_ROUTING" /&gt;
193&lt;uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /&gt;
194
195   AudioRecord mRecorder;
196   Handler mHandler = new Handler();
197   int mMinBufferSize = AudioRecord.getMinBufferSize(RECORD_SAMPLING_RATE,
198           AudioFormat.CHANNEL_IN_MONO,
199           AudioFormat.ENCODING_PCM_16BIT);;
200   static final int RECORD_SAMPLING_RATE = 48000;
201   public void doCapture() {
202       mRecorder = new AudioRecord(MediaRecorder.AudioSource.DEFAULT, RECORD_SAMPLING_RATE,
203               AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT, mMinBufferSize * 10);
204       AudioManager am = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
205       ArrayList&lt;AudioPort&gt; audioPorts = new ArrayList&lt;AudioPort&gt;();
206       am.listAudioPorts(audioPorts);
207       AudioPortConfig srcPortConfig = null;
208       AudioPortConfig sinkPortConfig = null;
209       for (AudioPort audioPort : audioPorts) {
210           if (srcPortConfig == null
211                   && audioPort.role() == AudioPort.ROLE_SOURCE
212                   && audioPort instanceof AudioDevicePort) {
213               AudioDevicePort audioDevicePort = (AudioDevicePort) audioPort;
214               if (audioDevicePort.type() == AudioManager.DEVICE_IN_LOOPBACK) {
215                   srcPortConfig = audioPort.buildConfig(48000, AudioFormat.CHANNEL_IN_DEFAULT,
216                           AudioFormat.ENCODING_DEFAULT, null);
217                   Log.d(LOG_TAG, "Found loopback audio source port : " + audioPort);
218               }
219           }
220           else if (sinkPortConfig == null
221                   && audioPort.role() == AudioPort.ROLE_SINK
222                   && audioPort instanceof AudioMixPort) {
223               sinkPortConfig = audioPort.buildConfig(48000, AudioFormat.CHANNEL_OUT_DEFAULT,
224                       AudioFormat.ENCODING_DEFAULT, null);
225               Log.d(LOG_TAG, "Found recorder audio mix port : " + audioPort);
226           }
227       }
228       if (srcPortConfig != null && sinkPortConfig != null) {
229           AudioPatch[] patches = new AudioPatch[] { null };
230           int status = am.createAudioPatch(
231                   patches,
232                   new AudioPortConfig[] { srcPortConfig },
233                   new AudioPortConfig[] { sinkPortConfig });
234           Log.d(LOG_TAG, "Result of createAudioPatch(): " + status);
235       }
236       mRecorder.startRecording();
237       processAudioData();
238       mRecorder.stop();
239       mRecorder.release();
240   }
241   private void processAudioData() {
242       OutputStream rawFileStream = null;
243       byte data[] = new byte[mMinBufferSize];
244       try {
245           rawFileStream = new BufferedOutputStream(
246                   new FileOutputStream(new File("/sdcard/record_loopback.raw")));
247       } catch (FileNotFoundException e) {
248           Log.d(LOG_TAG, "Can't open file.", e);
249       }
250       long startTimeMs = System.currentTimeMillis();
251       while (System.currentTimeMillis() - startTimeMs &lt; 5000) {
252           int nbytes = mRecorder.read(data, 0, mMinBufferSize);
253           if (nbytes &lt;= 0) {
254               continue;
255           }
256           try {
257               rawFileStream.write(data);
258           } catch (IOException e) {
259               Log.e(LOG_TAG, "Error on writing raw file.", e);
260           }
261       }
262       try {
263           rawFileStream.close();
264       } catch (IOException e) {
265       }
266       Log.d(LOG_TAG, "Exit audio recording.");
267   }
268</pre>
269
270<p>Locate the captured audio file in <code>/sdcard/record_loopback.raw</code> and listen to it using
271<code>ffmeg</code>:</p>
272
273<pre>
274adb pull /sdcard/record_loopback.raw
275ffmpeg -f s16le -ar 48k -ac 1 -i record_loopback.raw record_loopback.wav
276ffplay record_loopback.wav
277</pre>
278
279<h2 id="Use cases">Use cases</h2>
280
281<p>This section includes common use cases for TV audio.</p>
282
283<h3 id="tvSpeaker">TV tuner with speaker output</h3>
284
285<p>When a TV tuner becomes active, the audio routing API creates an audio patch between the tuner
286and the default output (e.g. the speaker). The tuner output does not require decoding, but final
287audio output is mixed with software output_stream.</p>
288
289<p><img src="images/ape_audio_tv_tuner.png" alt="Android TV Tuner Audio Patch" />
290<p class="img-caption">
291<strong>Figure 2.</strong> Audio Patch for TV tuner with speaker output.</p>
292
293
294<h3 id="hdmiOut">HDMI OUT during live TV</h3>
295
296<p>A user is watching live TV then switches to the HDMI audio output (Intent.ACTION_HDMI_AUDIO_PLUG)
297. The output device of all output_streams changes to the HDMI_OUT port, and the TIF manager changes
298the sink port of the existing tuner audio patch to the HDMI_OUT port.</p>
299
300<p><p><img src="images/ape_audio_tv_hdmi_tuner.png" alt="Android TV HDMI-OUT Audio Patch" />
301<p class="img-caption">
302<strong>Figure 3.</strong> Audio Patch for HDMI OUT from live TV.</p>
303