Tunnel Mode Playback (Fire TV Edition)

Fire TV Edition’s hardware decoder supports playback of 4K @ 60 FPS. To play back 4K @ 60 FPS, the timing requirement of media pipeline is aggressive and the app may not be able to render 4K frames at 16 msec interval due to thread and process scheduling limitations of the kernel. This may result into frame drops and a sub-par movie experience. To get the best out of the hardware, use Tunnel Mode playback.

How to Support Tunnel Mode Playback

The main changes required to support Tunnel Mode playback are as follows:

  1. Generate a AudioTrack session ID from AudioManager.

    audioSessionId = ((AudioManager)ctx.getSystemService(Context.AUDIO_SERVICE)).generateAudioSessionId();
    
  2. Configure Video Decoder for Tunnel mode:

    format.setFeatureEnabled(MediaCodecInfo.CodecCapabilities.FEATURE_TunneledPlayback, true);
    
  3. Configure Video decoder with the AudioTrack session ID.

    format.setInteger(android.media.MediaFormat.KEY_AUDIO_SESSION_ID, audioSessionId);
    
  4. Create AudioTrack with the session ID generated from AudioManager & set FLAG_HW_AV_SYNC flag.

    AudioAttributes attr = new AudioAttributes.Builder()
     .setLegacyStreamType(AudioManager.STREAM_MUSIC)
     .setFlags(AudioAttributes.FLAG_HW_AV_SYNC)
     .build();
    AudioFormat fmt = new AudioFormat.Builder()
     .setEncoding(targetEncoding)
     .setSampleRate(sampleRate)
     .setChannelMask(channelConfig)
     .build();
    AudioTrack audioTrack = new android.media.AudioTrack(attr, fmt, bufferSize, 575 android.media.AudioTrack.MODE_STREAM, audioSessionId);
    
  5. Do not call dequeueOutputBuffer and releaseOutputBuffer for video decoder.
  6. Insert an 16 byte AV Sync header to the audio data being written to AudioTrack.

    private ByteBuffer avSyncHeader = null;
    avSyncHeader = ByteBuffer.allocate(16);
    avSyncHeader.order(ByteOrder.BIG_ENDIAN);
    avSyncHeader.position(0);
    avSyncHeader.order(ByteOrder.BIG_ENDIAN);
    avSyncHeader.putInt(AVSYNC_START_CODE);
    avSyncHeader.putInt(bufferBytesRemaining);
    avSyncHeader.putLong(presentationTimeUs * 1000 );
    avSyncHeader.position(0);
    

This header contains a start code, presentation time (in nano secs), and size of data. Write this data before you write the audio data to audio track.

More Details about Tunnel Mode Playback

For more details refer to the Tunnel Mode Playback implementation in Exoplayer 2: