blob: fb16945d82c7602fdfdef586d5221c0809cda51a [file] [log] [blame]
/*
* 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 com.google.android.exoplayer2.metadata.dvbsi;
import androidx.annotation.Nullable;
import com.google.android.exoplayer2.metadata.Metadata;
import com.google.android.exoplayer2.metadata.MetadataInputBuffer;
import com.google.android.exoplayer2.metadata.SimpleMetadataDecoder;
import com.google.android.exoplayer2.util.ParsableBitArray;
import com.google.common.base.Charsets;
import java.nio.ByteBuffer;
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>.
*/
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, Charsets.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, Charsets.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);
}
}