public final class

AppInfoTableDecoder

extends SimpleMetadataDecoder

 java.lang.Object

androidx.media3.extractor.metadata.SimpleMetadataDecoder

↳androidx.media3.extractor.metadata.dvbsi.AppInfoTableDecoder

Gradle dependencies

compile group: 'androidx.media3', name: 'media3-extractor', version: '1.5.0-alpha01'

  • groupId: androidx.media3
  • artifactId: media3-extractor
  • version: 1.5.0-alpha01

Artifact androidx.media3:media3-extractor:1.5.0-alpha01 it located at Google repository (https://maven.google.com/)

Overview

Decoder for the DVB Application Information Table (AIT).

For more info on the AIT see section 5.3.4 of the DVB ETSI TS 102 809 v1.1.1 spec.

Summary

Fields
public static final intAPPLICATION_INFORMATION_TABLE_ID

See table 16 in section 5.3.4.6.

Constructors
publicAppInfoTableDecoder()

Methods
protected abstract Metadatadecode(MetadataInputBuffer inputBuffer, java.nio.ByteBuffer buffer)

Called by SimpleMetadataDecoder.decode(MetadataInputBuffer) after input buffer validation has been performed.

from SimpleMetadataDecoderdecode
from java.lang.Objectclone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait

Fields

public static final int APPLICATION_INFORMATION_TABLE_ID

See table 16 in section 5.3.4.6.

Constructors

public AppInfoTableDecoder()

Methods

protected abstract Metadata decode(MetadataInputBuffer inputBuffer, java.nio.ByteBuffer buffer)

Called by SimpleMetadataDecoder.decode(MetadataInputBuffer) after input buffer validation has been performed.

Parameters:

inputBuffer: The input buffer to decode.
buffer: The input buffer's data buffer, for convenience. Validation by SimpleMetadataDecoder.decode(MetadataInputBuffer) guarantees that hasArray, position and arrayOffset are true, 0 and 0 respectively.

Returns:

The decoded metadata object, or null if the metadata could not be decoded.

Source

/*
 * Copyright (C) 2020 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package androidx.media3.extractor.metadata.dvbsi;

import androidx.annotation.Nullable;
import androidx.media3.common.Metadata;
import androidx.media3.common.util.ParsableBitArray;
import androidx.media3.common.util.UnstableApi;
import androidx.media3.extractor.metadata.MetadataInputBuffer;
import androidx.media3.extractor.metadata.SimpleMetadataDecoder;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;

/**
 * Decoder for the DVB Application Information Table (AIT).
 *
 * <p>For more info on the AIT see section 5.3.4 of the <a
 * href="https://www.etsi.org/deliver/etsi_ts/102800_102899/102809/01.01.01_60/ts_102809v010101p.pdf">
 * DVB ETSI TS 102 809 v1.1.1 spec</a>.
 */
@UnstableApi
public final class AppInfoTableDecoder extends SimpleMetadataDecoder {

  /** See section 5.3.6. */
  private static final int DESCRIPTOR_TRANSPORT_PROTOCOL = 0x02;

  /** See section 5.3.7. */
  private static final int DESCRIPTOR_SIMPLE_APPLICATION_LOCATION = 0x15;

  /** See table 29 in section 5.3.6. */
  private static final int TRANSPORT_PROTOCOL_HTTP = 3;

  /** See table 16 in section 5.3.4.6. */
  public static final int APPLICATION_INFORMATION_TABLE_ID = 0x74;

  @Override
  @Nullable
  @SuppressWarnings("ByteBufferBackingArray") // Buffer validated by SimpleMetadataDecoder.decode
  protected Metadata decode(MetadataInputBuffer inputBuffer, ByteBuffer buffer) {
    int tableId = buffer.get();
    return tableId == APPLICATION_INFORMATION_TABLE_ID
        ? parseAit(new ParsableBitArray(buffer.array(), buffer.limit()))
        : null;
  }

  @Nullable
  private static Metadata parseAit(ParsableBitArray sectionData) {
    // tableId, section_syntax_indication, reserved_future_use, reserved
    sectionData.skipBits(12);
    int sectionLength = sectionData.readBits(12);
    int endOfSection = sectionData.getBytePosition() + sectionLength - 4 /* Ignore leading CRC */;

    // test_application_flag, application_type, reserved, version_number, current_next_indicator,
    // section_number, last_section_number, reserved_future_use
    sectionData.skipBits(44);

    int commonDescriptorsLength = sectionData.readBits(12);

    // Since we currently only keep URL and control code, which are unique per application,
    // there is no useful information in common descriptor.
    sectionData.skipBytes(commonDescriptorsLength);

    // reserved_future_use, application_loop_length
    sectionData.skipBits(16);

    ArrayList<AppInfoTable> appInfoTables = new ArrayList<>();
    while (sectionData.getBytePosition() < endOfSection) {
      @Nullable String urlBase = null;
      @Nullable String urlExtension = null;

      // application_identifier
      sectionData.skipBits(48);

      int controlCode = sectionData.readBits(8);

      // reserved_future_use
      sectionData.skipBits(4);

      int applicationDescriptorsLoopLength = sectionData.readBits(12);
      int positionOfNextApplication =
          sectionData.getBytePosition() + applicationDescriptorsLoopLength;
      while (sectionData.getBytePosition() < positionOfNextApplication) {
        int descriptorTag = sectionData.readBits(8);
        int descriptorLength = sectionData.readBits(8);
        int positionOfNextDescriptor = sectionData.getBytePosition() + descriptorLength;

        if (descriptorTag == DESCRIPTOR_TRANSPORT_PROTOCOL) {
          // See section 5.3.6.
          int protocolId = sectionData.readBits(16);
          // label
          sectionData.skipBits(8);

          if (protocolId == TRANSPORT_PROTOCOL_HTTP) {
            // See section 5.3.6.2.
            while (sectionData.getBytePosition() < positionOfNextDescriptor) {
              int urlBaseLength = sectionData.readBits(8);
              urlBase = sectionData.readBytesAsString(urlBaseLength, StandardCharsets.US_ASCII);

              int extensionCount = sectionData.readBits(8);
              for (int urlExtensionIndex = 0;
                  urlExtensionIndex < extensionCount;
                  urlExtensionIndex++) {
                int urlExtensionLength = sectionData.readBits(8);
                sectionData.skipBytes(urlExtensionLength);
              }
            }
          }
        } else if (descriptorTag == DESCRIPTOR_SIMPLE_APPLICATION_LOCATION) {
          // See section 5.3.7.
          urlExtension = sectionData.readBytesAsString(descriptorLength, StandardCharsets.US_ASCII);
        }

        sectionData.setPosition(positionOfNextDescriptor * 8);
      }

      sectionData.setPosition(positionOfNextApplication * 8);

      if (urlBase != null && urlExtension != null) {
        appInfoTables.add(new AppInfoTable(controlCode, urlBase + urlExtension));
      }
    }

    return appInfoTables.isEmpty() ? null : new Metadata(appInfoTables);
  }
}