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