Merge pull request #6922 from phhusson:feature/ait
PiperOrigin-RevId: 297579733
This commit is contained in:
commit
6946170d3e
@ -82,6 +82,89 @@
|
|||||||
[GL demo app](https://github.com/google/ExoPlayer/tree/dev-v2/demos/gl) to
|
[GL demo app](https://github.com/google/ExoPlayer/tree/dev-v2/demos/gl) to
|
||||||
show how to render video to a `GLSurfaceView` while applying a GL shader.
|
show how to render video to a `GLSurfaceView` while applying a GL shader.
|
||||||
([#6920](https://github.com/google/ExoPlayer/issues/6920)).
|
([#6920](https://github.com/google/ExoPlayer/issues/6920)).
|
||||||
|
* Core library:
|
||||||
|
* Add API in `AnalyticsListener` to report video frame processing offset.
|
||||||
|
`MediaCodecVideoRenderer` reports the event.
|
||||||
|
* Add fields `videoFrameProcessingOffsetUsSum` and
|
||||||
|
`videoFrameProcessingOffsetUsCount` in `DecoderCounters` to compute the
|
||||||
|
average video frame processing offset.
|
||||||
|
* Add playlist API
|
||||||
|
([#6161](https://github.com/google/ExoPlayer/issues/6161)).
|
||||||
|
* Add `play` and `pause` methods to `Player`.
|
||||||
|
* Add `Player.getCurrentLiveOffset` to conveniently return the live
|
||||||
|
offset.
|
||||||
|
* Add `Player.onPlayWhenReadyChanged` with reasons.
|
||||||
|
* Add `Player.onPlaybackStateChanged` and deprecate
|
||||||
|
`Player.onPlayerStateChanged`.
|
||||||
|
* Deprecate and rename `getPlaybackError` to `getPlayerError` for
|
||||||
|
consistency.
|
||||||
|
* Deprecate and rename `onLoadingChanged` to `onIsLoadingChanged` for
|
||||||
|
consistency.
|
||||||
|
* Make `MediaSourceEventListener.LoadEventInfo` and
|
||||||
|
`MediaSourceEventListener.MediaLoadData` top-level classes.
|
||||||
|
* Rename `MediaCodecRenderer.onOutputFormatChanged` to
|
||||||
|
`MediaCodecRenderer.onOutputMediaFormatChanged`, further clarifying the
|
||||||
|
distinction between `Format` and `MediaFormat`.
|
||||||
|
* Move player message-related constants from `C` to `Renderer`, to avoid
|
||||||
|
having the constants class depend on player/renderer classes.
|
||||||
|
* Split out `common` and `extractor` submodules.
|
||||||
|
* Allow to explicitly send `PlayerMessage`s at the end of a stream.
|
||||||
|
* Add `DataSpec.Builder` and deprecate most `DataSpec` constructors.
|
||||||
|
* Add `DataSpec.customData` to allow applications to pass custom data
|
||||||
|
through `DataSource` chains.
|
||||||
|
* Add a sample count parameter to `MediaCodecRenderer.processOutputBuffer`
|
||||||
|
and `AudioSink.handleBuffer` to allow batching multiple encoded frames
|
||||||
|
in one buffer.
|
||||||
|
* Add a `Format.Builder` and deprecate all `Format.create*` methods and
|
||||||
|
most `Format.copyWith*` methods.
|
||||||
|
* Split `Format.bitrate` into `Format.averageBitrate` and
|
||||||
|
`Format.peakBitrate`
|
||||||
|
([#2863](https://github.com/google/ExoPlayer/issues/2863)).
|
||||||
|
* Text:
|
||||||
|
* Parse `<ruby>` and `<rt>` tags in WebVTT subtitles (rendering is coming
|
||||||
|
later).
|
||||||
|
* Parse `text-combine-upright` CSS property (i.e. tate-chu-yoko) in WebVTT
|
||||||
|
subtitles (rendering is coming later).
|
||||||
|
* Parse `tts:combineText` property (i.e. tate-chu-yoko) in TTML subtitles
|
||||||
|
(rendering is coming later).
|
||||||
|
* Fix `SubtitlePainter` to render `EDGE_TYPE_OUTLINE` using the correct
|
||||||
|
color ([#6724](https://github.com/google/ExoPlayer/pull/6724)).
|
||||||
|
* Add support for WebVTT default
|
||||||
|
[text](https://www.w3.org/TR/webvtt1/#default-text-color) and
|
||||||
|
[background](https://www.w3.org/TR/webvtt1/#default-text-background)
|
||||||
|
colors ([PR #4178](https://github.com/google/ExoPlayer/pull/4178),
|
||||||
|
[issue #6581](https://github.com/google/ExoPlayer/issues/6581)).
|
||||||
|
* Catch-and-log all fatal exceptions in `TextRenderer` instead of
|
||||||
|
re-throwing, allowing playback to continue even if subtitles fail
|
||||||
|
([#6885](https://github.com/google/ExoPlayer/issues/6885)).
|
||||||
|
* Parse `tts:ruby` and `tts:rubyPosition` properties in TTML subtitles
|
||||||
|
(rendering is coming later).
|
||||||
|
* DRM:
|
||||||
|
* Add support for attaching DRM sessions to clear content in the demo app.
|
||||||
|
* Remove `DrmSessionManager` references from all renderers.
|
||||||
|
`DrmSessionManager` must be injected into the MediaSources using the
|
||||||
|
MediaSources factories.
|
||||||
|
* Downloads: Merge downloads in `SegmentDownloader` to improve overall
|
||||||
|
download speed ([#5978](https://github.com/google/ExoPlayer/issues/5978)).
|
||||||
|
* MP3: Add `IndexSeeker` for accurate seeks in VBR streams
|
||||||
|
([#6787](https://github.com/google/ExoPlayer/issues/6787)). This seeker is
|
||||||
|
enabled by passing `FLAG_ENABLE_INDEX_SEEKING` to the `Mp3Extractor`. It may
|
||||||
|
require to scan a significant portion of the file for seeking, which may be
|
||||||
|
costly on large files.
|
||||||
|
* MP4: Store the Android capture frame rate only in `Format.metadata`.
|
||||||
|
`Format.frameRate` now stores the calculated frame rate.
|
||||||
|
* Testing
|
||||||
|
* Upgrade Truth dependency from 0.44 to 1.0.
|
||||||
|
* Upgrade to JUnit 4.13-rc-2.
|
||||||
|
* UI
|
||||||
|
* Move logic of prev, next, fast forward and rewind to ControlDispatcher
|
||||||
|
([#6926](https://github.com/google/ExoPlayer/issues/6926)).
|
||||||
|
* Demo apps: Add
|
||||||
|
[GL demo app](https://github.com/google/ExoPlayer/tree/dev-v2/demos/gl) to
|
||||||
|
show how to render video to a `GLSurfaceView` while applying a GL shader.
|
||||||
|
([#6920](https://github.com/google/ExoPlayer/issues/6920)).
|
||||||
|
* Metadata: Add minimal DVB Application Information Table (AIT) support
|
||||||
|
([#6922](https://github.com/google/ExoPlayer/pull/6922)).
|
||||||
|
|
||||||
### 2.11.3 (2020-02-19) ###
|
### 2.11.3 (2020-02-19) ###
|
||||||
|
|
||||||
|
@ -97,6 +97,7 @@ public final class MimeTypes {
|
|||||||
public static final String APPLICATION_DVBSUBS = BASE_TYPE_APPLICATION + "/dvbsubs";
|
public static final String APPLICATION_DVBSUBS = BASE_TYPE_APPLICATION + "/dvbsubs";
|
||||||
public static final String APPLICATION_EXIF = BASE_TYPE_APPLICATION + "/x-exif";
|
public static final String APPLICATION_EXIF = BASE_TYPE_APPLICATION + "/x-exif";
|
||||||
public static final String APPLICATION_ICY = BASE_TYPE_APPLICATION + "/x-icy";
|
public static final String APPLICATION_ICY = BASE_TYPE_APPLICATION + "/x-icy";
|
||||||
|
public static final String APPLICATION_AIT = BASE_TYPE_APPLICATION + "/vnd.dvb.ait";
|
||||||
|
|
||||||
private static final ArrayList<CustomMimeType> customMimeTypes = new ArrayList<>();
|
private static final ArrayList<CustomMimeType> customMimeTypes = new ArrayList<>();
|
||||||
|
|
||||||
|
@ -15,6 +15,9 @@
|
|||||||
*/
|
*/
|
||||||
package com.google.android.exoplayer2.util;
|
package com.google.android.exoplayer2.util;
|
||||||
|
|
||||||
|
import com.google.android.exoplayer2.C;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wraps a byte array, providing methods that allow it to be read as a bitstream.
|
* Wraps a byte array, providing methods that allow it to be read as a bitstream.
|
||||||
*/
|
*/
|
||||||
@ -277,6 +280,31 @@ public final class ParsableBitArray {
|
|||||||
assertValidOffset();
|
assertValidOffset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads the next {@code length} bytes as a UTF-8 string. Must only be called when the position is
|
||||||
|
* byte aligned.
|
||||||
|
*
|
||||||
|
* @param length The number of bytes to read.
|
||||||
|
* @return The string encoded by the bytes in UTF-8.
|
||||||
|
*/
|
||||||
|
public String readBytesAsString(int length) {
|
||||||
|
return readBytesAsString(length, Charset.forName(C.UTF8_NAME));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads the next {@code length} bytes as a string encoded in {@link Charset}. Must only be called
|
||||||
|
* when the position is byte aligned.
|
||||||
|
*
|
||||||
|
* @param length The number of bytes to read.
|
||||||
|
* @param charset The character set of the encoded characters.
|
||||||
|
* @return The string encoded by the bytes in the specified character set.
|
||||||
|
*/
|
||||||
|
public String readBytesAsString(int length, Charset charset) {
|
||||||
|
byte[] bytes = new byte[length];
|
||||||
|
readBytes(bytes, 0, length);
|
||||||
|
return new String(bytes, charset);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Overwrites {@code numBits} from this array using the {@code numBits} least significant bits
|
* Overwrites {@code numBits} from this array using the {@code numBits} least significant bits
|
||||||
* from {@code value}. Bits are written in order from most significant to least significant. The
|
* from {@code value}. Bits are written in order from most significant to least significant. The
|
||||||
|
@ -16,9 +16,12 @@
|
|||||||
package com.google.android.exoplayer2.util;
|
package com.google.android.exoplayer2.util;
|
||||||
|
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
import static org.junit.Assert.assertThrows;
|
||||||
|
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
|
import com.google.android.exoplayer2.C;
|
||||||
import com.google.android.exoplayer2.testutil.TestUtil;
|
import com.google.android.exoplayer2.testutil.TestUtil;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
@ -276,6 +279,35 @@ public final class ParsableBitArrayTest {
|
|||||||
assertThat(testArray.readBits(8)).isEqualTo(0x5F);
|
assertThat(testArray.readBits(8)).isEqualTo(0x5F);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testReadBytesAsStringDefaultsToUtf8() {
|
||||||
|
byte[] testData = "a non-åscii strìng".getBytes(Charset.forName(C.UTF8_NAME));
|
||||||
|
ParsableBitArray testArray = new ParsableBitArray(testData);
|
||||||
|
|
||||||
|
testArray.skipBytes(2);
|
||||||
|
assertThat(testArray.readBytesAsString(testData.length - 2)).isEqualTo("non-åscii strìng");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testReadBytesAsStringExplicitCharset() {
|
||||||
|
byte[] testData = "a non-åscii strìng".getBytes(Charset.forName(C.UTF16_NAME));
|
||||||
|
ParsableBitArray testArray = new ParsableBitArray(testData);
|
||||||
|
|
||||||
|
testArray.skipBytes(6);
|
||||||
|
assertThat(testArray.readBytesAsString(testData.length - 6, Charset.forName(C.UTF16_NAME)))
|
||||||
|
.isEqualTo("non-åscii strìng");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testReadBytesNotByteAligned() {
|
||||||
|
String testString = "test string";
|
||||||
|
byte[] testData = testString.getBytes(Charset.forName(C.UTF8_NAME));
|
||||||
|
ParsableBitArray testArray = new ParsableBitArray(testData);
|
||||||
|
|
||||||
|
testArray.skipBit();
|
||||||
|
assertThrows(IllegalStateException.class, () -> testArray.readBytesAsString(2));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testPutBitsWithinByte() {
|
public void testPutBitsWithinByte() {
|
||||||
ParsableBitArray output = new ParsableBitArray(new byte[4]);
|
ParsableBitArray output = new ParsableBitArray(new byte[4]);
|
||||||
|
@ -17,6 +17,7 @@ package com.google.android.exoplayer2.metadata;
|
|||||||
|
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import com.google.android.exoplayer2.Format;
|
import com.google.android.exoplayer2.Format;
|
||||||
|
import com.google.android.exoplayer2.metadata.dvbsi.AppInfoTableDecoder;
|
||||||
import com.google.android.exoplayer2.metadata.emsg.EventMessageDecoder;
|
import com.google.android.exoplayer2.metadata.emsg.EventMessageDecoder;
|
||||||
import com.google.android.exoplayer2.metadata.icy.IcyDecoder;
|
import com.google.android.exoplayer2.metadata.icy.IcyDecoder;
|
||||||
import com.google.android.exoplayer2.metadata.id3.Id3Decoder;
|
import com.google.android.exoplayer2.metadata.id3.Id3Decoder;
|
||||||
@ -67,7 +68,8 @@ public interface MetadataDecoderFactory {
|
|||||||
return MimeTypes.APPLICATION_ID3.equals(mimeType)
|
return MimeTypes.APPLICATION_ID3.equals(mimeType)
|
||||||
|| MimeTypes.APPLICATION_EMSG.equals(mimeType)
|
|| MimeTypes.APPLICATION_EMSG.equals(mimeType)
|
||||||
|| MimeTypes.APPLICATION_SCTE35.equals(mimeType)
|
|| MimeTypes.APPLICATION_SCTE35.equals(mimeType)
|
||||||
|| MimeTypes.APPLICATION_ICY.equals(mimeType);
|
|| MimeTypes.APPLICATION_ICY.equals(mimeType)
|
||||||
|
|| MimeTypes.APPLICATION_AIT.equals(mimeType);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -83,6 +85,8 @@ public interface MetadataDecoderFactory {
|
|||||||
return new SpliceInfoDecoder();
|
return new SpliceInfoDecoder();
|
||||||
case MimeTypes.APPLICATION_ICY:
|
case MimeTypes.APPLICATION_ICY:
|
||||||
return new IcyDecoder();
|
return new IcyDecoder();
|
||||||
|
case MimeTypes.APPLICATION_AIT:
|
||||||
|
return new AppInfoTableDecoder();
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,80 @@
|
|||||||
|
/*
|
||||||
|
* 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 android.os.Parcel;
|
||||||
|
import android.os.Parcelable;
|
||||||
|
import com.google.android.exoplayer2.metadata.Metadata;
|
||||||
|
import com.google.android.exoplayer2.util.Assertions;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A representation of a 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 AppInfoTable implements Metadata.Entry {
|
||||||
|
/**
|
||||||
|
* The application shall be started when the service is selected, unless the application is
|
||||||
|
* already running.
|
||||||
|
*/
|
||||||
|
public static final int CONTROL_CODE_AUTOSTART = 0x01;
|
||||||
|
/**
|
||||||
|
* The application is allowed to run while the service is selected, however it shall not start
|
||||||
|
* automatically when the service becomes selected.
|
||||||
|
*/
|
||||||
|
public static final int CONTROL_CODE_PRESENT = 0x02;
|
||||||
|
|
||||||
|
public final int controlCode;
|
||||||
|
public final String url;
|
||||||
|
|
||||||
|
public AppInfoTable(int controlCode, String url) {
|
||||||
|
this.controlCode = controlCode;
|
||||||
|
this.url = url;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "Ait(controlCode=" + controlCode + ",url=" + url + ")";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int describeContents() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeToParcel(Parcel parcel, int i) {
|
||||||
|
parcel.writeString(url);
|
||||||
|
parcel.writeInt(controlCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final Parcelable.Creator<AppInfoTable> CREATOR =
|
||||||
|
new Parcelable.Creator<AppInfoTable>() {
|
||||||
|
@Override
|
||||||
|
public AppInfoTable createFromParcel(Parcel in) {
|
||||||
|
String url = Assertions.checkNotNull(in.readString());
|
||||||
|
int controlCode = in.readInt();
|
||||||
|
return new AppInfoTable(controlCode, url);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AppInfoTable[] newArray(int size) {
|
||||||
|
return new AppInfoTable[size];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
@ -0,0 +1,140 @@
|
|||||||
|
/*
|
||||||
|
* 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.C;
|
||||||
|
import com.google.android.exoplayer2.metadata.Metadata;
|
||||||
|
import com.google.android.exoplayer2.metadata.MetadataDecoder;
|
||||||
|
import com.google.android.exoplayer2.metadata.MetadataInputBuffer;
|
||||||
|
import com.google.android.exoplayer2.util.Assertions;
|
||||||
|
import com.google.android.exoplayer2.util.ParsableBitArray;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
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 implements MetadataDecoder {
|
||||||
|
|
||||||
|
/** 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")
|
||||||
|
public Metadata decode(MetadataInputBuffer inputBuffer) {
|
||||||
|
ByteBuffer buffer = Assertions.checkNotNull(inputBuffer.data);
|
||||||
|
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, Charset.forName(C.ASCII_NAME));
|
||||||
|
|
||||||
|
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, Charset.forName(C.ASCII_NAME));
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
@NonNullApi
|
||||||
|
package com.google.android.exoplayer2.metadata.dvbsi;
|
||||||
|
|
||||||
|
import com.google.android.exoplayer2.util.NonNullApi;
|
@ -0,0 +1,83 @@
|
|||||||
|
/*
|
||||||
|
* 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 static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
|
import androidx.test.core.app.ApplicationProvider;
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
|
import com.google.android.exoplayer2.metadata.Metadata;
|
||||||
|
import com.google.android.exoplayer2.metadata.MetadataInputBuffer;
|
||||||
|
import com.google.android.exoplayer2.testutil.TestUtil;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
/** Tests for {@link AppInfoTableDecoder}. */
|
||||||
|
@RunWith(AndroidJUnit4.class)
|
||||||
|
public final class AppInfoTableDecoderTest {
|
||||||
|
|
||||||
|
private static final String TYPICAL_FILE = "dvbsi/ait_typical.bin";
|
||||||
|
private static final String NO_URL_BASE_FILE = "dvbsi/ait_no_url_base.bin";
|
||||||
|
private static final String NO_URL_PATH_FILE = "dvbsi/ait_no_url_path.bin";
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void decode_typical() throws Exception {
|
||||||
|
AppInfoTableDecoder decoder = new AppInfoTableDecoder();
|
||||||
|
Metadata metadata = decoder.decode(createMetadataInputBuffer(readTestFile(TYPICAL_FILE)));
|
||||||
|
|
||||||
|
assertThat(metadata.length()).isEqualTo(2);
|
||||||
|
Metadata.Entry firstEntry = metadata.get(0);
|
||||||
|
assertThat(firstEntry).isInstanceOf(AppInfoTable.class);
|
||||||
|
assertThat(((AppInfoTable) firstEntry).controlCode)
|
||||||
|
.isEqualTo(AppInfoTable.CONTROL_CODE_AUTOSTART);
|
||||||
|
assertThat(((AppInfoTable) firstEntry).url).isEqualTo("http://example.com/path/foo");
|
||||||
|
Metadata.Entry secondEntry = metadata.get(1);
|
||||||
|
assertThat(secondEntry).isInstanceOf(AppInfoTable.class);
|
||||||
|
assertThat(((AppInfoTable) secondEntry).controlCode)
|
||||||
|
.isEqualTo(AppInfoTable.CONTROL_CODE_PRESENT);
|
||||||
|
assertThat(((AppInfoTable) secondEntry).url).isEqualTo("http://google.com/path/bar");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void decode_noUrlBase() throws Exception {
|
||||||
|
AppInfoTableDecoder decoder = new AppInfoTableDecoder();
|
||||||
|
Metadata metadata = decoder.decode(createMetadataInputBuffer(readTestFile(NO_URL_BASE_FILE)));
|
||||||
|
|
||||||
|
assertThat(metadata).isNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void decode_noUrlPath() throws Exception {
|
||||||
|
AppInfoTableDecoder decoder = new AppInfoTableDecoder();
|
||||||
|
Metadata metadata = decoder.decode(createMetadataInputBuffer(readTestFile(NO_URL_PATH_FILE)));
|
||||||
|
|
||||||
|
assertThat(metadata).isNull();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static MetadataInputBuffer createMetadataInputBuffer(byte[] data) {
|
||||||
|
MetadataInputBuffer inputBuffer = new MetadataInputBuffer();
|
||||||
|
inputBuffer.data = ByteBuffer.allocate(data.length);
|
||||||
|
inputBuffer.data.put(data);
|
||||||
|
inputBuffer.data.flip();
|
||||||
|
return inputBuffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte[] readTestFile(String name) throws IOException {
|
||||||
|
return TestUtil.getByteArray(ApplicationProvider.getApplicationContext(), name);
|
||||||
|
}
|
||||||
|
}
|
@ -80,7 +80,10 @@ public final class DefaultTsPayloadReaderFactory implements TsPayloadReader.Fact
|
|||||||
* delimiters (AUDs).
|
* delimiters (AUDs).
|
||||||
*/
|
*/
|
||||||
public static final int FLAG_DETECT_ACCESS_UNITS = 1 << 3;
|
public static final int FLAG_DETECT_ACCESS_UNITS = 1 << 3;
|
||||||
/** Prevents the creation of {@link SpliceInfoSectionReader} instances. */
|
/**
|
||||||
|
* Prevents the creation of {@link SectionPayloadReader}s for splice information sections
|
||||||
|
* (SCTE-35).
|
||||||
|
*/
|
||||||
public static final int FLAG_IGNORE_SPLICE_INFO_STREAM = 1 << 4;
|
public static final int FLAG_IGNORE_SPLICE_INFO_STREAM = 1 << 4;
|
||||||
/**
|
/**
|
||||||
* Whether the list of {@code closedCaptionFormats} passed to {@link
|
* Whether the list of {@code closedCaptionFormats} passed to {@link
|
||||||
@ -170,22 +173,25 @@ public final class DefaultTsPayloadReaderFactory implements TsPayloadReader.Fact
|
|||||||
return new PesReader(new H265Reader(buildSeiReader(esInfo)));
|
return new PesReader(new H265Reader(buildSeiReader(esInfo)));
|
||||||
case TsExtractor.TS_STREAM_TYPE_SPLICE_INFO:
|
case TsExtractor.TS_STREAM_TYPE_SPLICE_INFO:
|
||||||
return isSet(FLAG_IGNORE_SPLICE_INFO_STREAM)
|
return isSet(FLAG_IGNORE_SPLICE_INFO_STREAM)
|
||||||
? null : new SectionReader(new SpliceInfoSectionReader());
|
? null
|
||||||
|
: new SectionReader(new PassthroughSectionPayloadReader(MimeTypes.APPLICATION_SCTE35));
|
||||||
case TsExtractor.TS_STREAM_TYPE_ID3:
|
case TsExtractor.TS_STREAM_TYPE_ID3:
|
||||||
return new PesReader(new Id3Reader());
|
return new PesReader(new Id3Reader());
|
||||||
case TsExtractor.TS_STREAM_TYPE_DVBSUBS:
|
case TsExtractor.TS_STREAM_TYPE_DVBSUBS:
|
||||||
return new PesReader(
|
return new PesReader(
|
||||||
new DvbSubtitleReader(esInfo.dvbSubtitleInfos));
|
new DvbSubtitleReader(esInfo.dvbSubtitleInfos));
|
||||||
|
case TsExtractor.TS_STREAM_TYPE_AIT:
|
||||||
|
return new SectionReader(new PassthroughSectionPayloadReader(MimeTypes.APPLICATION_AIT));
|
||||||
default:
|
default:
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If {@link #FLAG_OVERRIDE_CAPTION_DESCRIPTORS} is set, returns a {@link SeiReader} for
|
* If {@link #FLAG_OVERRIDE_CAPTION_DESCRIPTORS} is set, returns a {@link SeiReader} for {@link
|
||||||
* {@link #closedCaptionFormats}. If unset, parses the PMT descriptor information and returns a
|
* #closedCaptionFormats}. If unset, parses the PMT descriptor information and returns a {@link
|
||||||
* {@link SeiReader} for the declared formats, or {@link #closedCaptionFormats} if the descriptor
|
* SeiReader} for the declared formats, or {@link #closedCaptionFormats} if the descriptor is not
|
||||||
* is not present.
|
* present.
|
||||||
*
|
*
|
||||||
* @param esInfo The {@link EsInfo} passed to {@link #createPayloadReader(int, EsInfo)}.
|
* @param esInfo The {@link EsInfo} passed to {@link #createPayloadReader(int, EsInfo)}.
|
||||||
* @return A {@link SeiReader} for closed caption tracks.
|
* @return A {@link SeiReader} for closed caption tracks.
|
||||||
|
@ -20,7 +20,6 @@ import com.google.android.exoplayer2.Format;
|
|||||||
import com.google.android.exoplayer2.extractor.ExtractorOutput;
|
import com.google.android.exoplayer2.extractor.ExtractorOutput;
|
||||||
import com.google.android.exoplayer2.extractor.TrackOutput;
|
import com.google.android.exoplayer2.extractor.TrackOutput;
|
||||||
import com.google.android.exoplayer2.util.Assertions;
|
import com.google.android.exoplayer2.util.Assertions;
|
||||||
import com.google.android.exoplayer2.util.MimeTypes;
|
|
||||||
import com.google.android.exoplayer2.util.ParsableByteArray;
|
import com.google.android.exoplayer2.util.ParsableByteArray;
|
||||||
import com.google.android.exoplayer2.util.TimestampAdjuster;
|
import com.google.android.exoplayer2.util.TimestampAdjuster;
|
||||||
import com.google.android.exoplayer2.util.Util;
|
import com.google.android.exoplayer2.util.Util;
|
||||||
@ -28,16 +27,30 @@ import org.checkerframework.checker.nullness.qual.EnsuresNonNull;
|
|||||||
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses splice info sections as defined by SCTE35.
|
* A {@link SectionPayloadReader} that directly outputs the section bytes as sample data.
|
||||||
|
*
|
||||||
|
* <p>Timestamp adjustment is provided through {@link Format#subsampleOffsetUs}.
|
||||||
*/
|
*/
|
||||||
public final class SpliceInfoSectionReader implements SectionPayloadReader {
|
public final class PassthroughSectionPayloadReader implements SectionPayloadReader {
|
||||||
|
|
||||||
|
private final String mimeType;
|
||||||
private @MonotonicNonNull TimestampAdjuster timestampAdjuster;
|
private @MonotonicNonNull TimestampAdjuster timestampAdjuster;
|
||||||
private @MonotonicNonNull TrackOutput output;
|
private @MonotonicNonNull TrackOutput output;
|
||||||
private boolean formatDeclared;
|
private boolean formatDeclared;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new PassthroughSectionPayloadReader.
|
||||||
|
*
|
||||||
|
* @param mimeType The MIME type set as {@link Format#sampleMimeType} on the created output track.
|
||||||
|
*/
|
||||||
|
public PassthroughSectionPayloadReader(String mimeType) {
|
||||||
|
this.mimeType = mimeType;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void init(TimestampAdjuster timestampAdjuster, ExtractorOutput extractorOutput,
|
public void init(
|
||||||
|
TimestampAdjuster timestampAdjuster,
|
||||||
|
ExtractorOutput extractorOutput,
|
||||||
TsPayloadReader.TrackIdGenerator idGenerator) {
|
TsPayloadReader.TrackIdGenerator idGenerator) {
|
||||||
this.timestampAdjuster = timestampAdjuster;
|
this.timestampAdjuster = timestampAdjuster;
|
||||||
idGenerator.generateNewId();
|
idGenerator.generateNewId();
|
||||||
@ -53,14 +66,20 @@ public final class SpliceInfoSectionReader implements SectionPayloadReader {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
output.format(
|
output.format(
|
||||||
Format.createSampleFormat(null, MimeTypes.APPLICATION_SCTE35)
|
new Format.Builder()
|
||||||
.copyWithSubsampleOffsetUs(timestampAdjuster.getTimestampOffsetUs()));
|
.setSampleMimeType(mimeType)
|
||||||
|
.setSubsampleOffsetUs(timestampAdjuster.getTimestampOffsetUs())
|
||||||
|
.build());
|
||||||
formatDeclared = true;
|
formatDeclared = true;
|
||||||
}
|
}
|
||||||
int sampleSize = sectionData.bytesLeft();
|
int sampleSize = sectionData.bytesLeft();
|
||||||
output.sampleData(sectionData, sampleSize);
|
output.sampleData(sectionData, sampleSize);
|
||||||
output.sampleMetadata(timestampAdjuster.getLastAdjustedTimestampUs(), C.BUFFER_FLAG_KEY_FRAME,
|
output.sampleMetadata(
|
||||||
sampleSize, 0, null);
|
timestampAdjuster.getLastAdjustedTimestampUs(),
|
||||||
|
C.BUFFER_FLAG_KEY_FRAME,
|
||||||
|
sampleSize,
|
||||||
|
0,
|
||||||
|
null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@EnsuresNonNull({"timestampAdjuster", "output"})
|
@EnsuresNonNull({"timestampAdjuster", "output"})
|
@ -96,6 +96,9 @@ public final class TsExtractor implements Extractor {
|
|||||||
public static final int TS_STREAM_TYPE_SPLICE_INFO = 0x86;
|
public static final int TS_STREAM_TYPE_SPLICE_INFO = 0x86;
|
||||||
public static final int TS_STREAM_TYPE_DVBSUBS = 0x59;
|
public static final int TS_STREAM_TYPE_DVBSUBS = 0x59;
|
||||||
|
|
||||||
|
// Stream types that aren't defined by the MPEG-2 TS specification.
|
||||||
|
public static final int TS_STREAM_TYPE_AIT = 0x101;
|
||||||
|
|
||||||
public static final int TS_PACKET_SIZE = 188;
|
public static final int TS_PACKET_SIZE = 188;
|
||||||
public static final int TS_SYNC_BYTE = 0x47; // First byte of each TS packet.
|
public static final int TS_SYNC_BYTE = 0x47; // First byte of each TS packet.
|
||||||
|
|
||||||
@ -494,6 +497,7 @@ public final class TsExtractor implements Extractor {
|
|||||||
private static final int TS_PMT_DESC_REGISTRATION = 0x05;
|
private static final int TS_PMT_DESC_REGISTRATION = 0x05;
|
||||||
private static final int TS_PMT_DESC_ISO639_LANG = 0x0A;
|
private static final int TS_PMT_DESC_ISO639_LANG = 0x0A;
|
||||||
private static final int TS_PMT_DESC_AC3 = 0x6A;
|
private static final int TS_PMT_DESC_AC3 = 0x6A;
|
||||||
|
private static final int TS_PMT_DESC_AIT = 0x6F;
|
||||||
private static final int TS_PMT_DESC_EAC3 = 0x7A;
|
private static final int TS_PMT_DESC_EAC3 = 0x7A;
|
||||||
private static final int TS_PMT_DESC_DTS = 0x7B;
|
private static final int TS_PMT_DESC_DTS = 0x7B;
|
||||||
private static final int TS_PMT_DESC_DVB_EXT = 0x7F;
|
private static final int TS_PMT_DESC_DVB_EXT = 0x7F;
|
||||||
@ -578,7 +582,7 @@ public final class TsExtractor implements Extractor {
|
|||||||
pmtScratch.skipBits(4); // reserved
|
pmtScratch.skipBits(4); // reserved
|
||||||
int esInfoLength = pmtScratch.readBits(12); // ES_info_length.
|
int esInfoLength = pmtScratch.readBits(12); // ES_info_length.
|
||||||
EsInfo esInfo = readEsInfo(sectionData, esInfoLength);
|
EsInfo esInfo = readEsInfo(sectionData, esInfoLength);
|
||||||
if (streamType == 0x06) {
|
if (streamType == 0x06 || streamType == 0x05) {
|
||||||
streamType = esInfo.streamType;
|
streamType = esInfo.streamType;
|
||||||
}
|
}
|
||||||
remainingEntriesLength -= esInfoLength + 5;
|
remainingEntriesLength -= esInfoLength + 5;
|
||||||
@ -688,6 +692,8 @@ public final class TsExtractor implements Extractor {
|
|||||||
dvbSubtitleInfos.add(new DvbSubtitleInfo(dvbLanguage, dvbSubtitlingType,
|
dvbSubtitleInfos.add(new DvbSubtitleInfo(dvbLanguage, dvbSubtitlingType,
|
||||||
initializationData));
|
initializationData));
|
||||||
}
|
}
|
||||||
|
} else if (descriptorTag == TS_PMT_DESC_AIT) {
|
||||||
|
streamType = TS_STREAM_TYPE_AIT;
|
||||||
}
|
}
|
||||||
// Skip unused bytes of current descriptor.
|
// Skip unused bytes of current descriptor.
|
||||||
data.skipBytes(positionOfNextDescriptor - data.getPosition());
|
data.skipBytes(positionOfNextDescriptor - data.getPosition());
|
||||||
|
@ -53,6 +53,11 @@ public final class TsExtractorTest {
|
|||||||
ExtractorAsserts.assertBehavior(TsExtractor::new, "ts/sample_scte35.ts");
|
ExtractorAsserts.assertBehavior(TsExtractor::new, "ts/sample_scte35.ts");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAit() throws Exception {
|
||||||
|
ExtractorAsserts.assertBehavior(TsExtractor::new, "ts/sample_ait.ts");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testStreamWithJunkData() throws Exception {
|
public void testStreamWithJunkData() throws Exception {
|
||||||
ExtractorAsserts.assertBehavior(
|
ExtractorAsserts.assertBehavior(
|
||||||
|
15
testdata/src/test/assets/dvbsi/README.md
vendored
Normal file
15
testdata/src/test/assets/dvbsi/README.md
vendored
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
# DVB Test Data
|
||||||
|
|
||||||
|
The `.bin` files in this directory are generated from the `.xml` files using
|
||||||
|
`tstabcomp` from [TSDuck](https://tsduck.io/).
|
||||||
|
|
||||||
|
The XML files are kept to make it clear where the values in the test assertions
|
||||||
|
are coming from, and to make it easier to change or add data in future. When
|
||||||
|
adding new files, or making changes to existing ones, you should regenerate the
|
||||||
|
`.bin` files using the command above before committing.
|
||||||
|
|
||||||
|
To regenerate all the `.bin` files:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
$ tstabcomp -c testdata/src/test/assets/dvbsi/*.xml
|
||||||
|
```
|
BIN
testdata/src/test/assets/dvbsi/ait_no_url_base.bin
vendored
Normal file
BIN
testdata/src/test/assets/dvbsi/ait_no_url_base.bin
vendored
Normal file
Binary file not shown.
13
testdata/src/test/assets/dvbsi/ait_no_url_base.xml
vendored
Normal file
13
testdata/src/test/assets/dvbsi/ait_no_url_base.xml
vendored
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<tsduck>
|
||||||
|
<AIT version="30" current="true" test_application_flag="false" application_type="0x0010">
|
||||||
|
<application control_code="0x01">
|
||||||
|
<application_identifier organization_id="0x00000120" application_id="0x0071"/>
|
||||||
|
<transport_protocol_descriptor transport_protocol_label="0x00">
|
||||||
|
<http>
|
||||||
|
</http>
|
||||||
|
</transport_protocol_descriptor>
|
||||||
|
<simple_application_location_descriptor initial_path="foo/bar"/>
|
||||||
|
</application>
|
||||||
|
</AIT>
|
||||||
|
</tsduck>
|
BIN
testdata/src/test/assets/dvbsi/ait_no_url_path.bin
vendored
Normal file
BIN
testdata/src/test/assets/dvbsi/ait_no_url_path.bin
vendored
Normal file
Binary file not shown.
29
testdata/src/test/assets/dvbsi/ait_no_url_path.xml
vendored
Normal file
29
testdata/src/test/assets/dvbsi/ait_no_url_path.xml
vendored
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!--
|
||||||
|
~ 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.
|
||||||
|
~
|
||||||
|
-->
|
||||||
|
<tsduck>
|
||||||
|
<AIT version="30" current="true" test_application_flag="false" application_type="0x0010">
|
||||||
|
<application control_code="0x01">
|
||||||
|
<application_identifier organization_id="0x00000120" application_id="0x0071"/>
|
||||||
|
<transport_protocol_descriptor transport_protocol_label="0x00">
|
||||||
|
<http>
|
||||||
|
<url base="http://google.com/"/>
|
||||||
|
</http>
|
||||||
|
</transport_protocol_descriptor>
|
||||||
|
</application>
|
||||||
|
</AIT>
|
||||||
|
</tsduck>
|
BIN
testdata/src/test/assets/dvbsi/ait_typical.bin
vendored
Normal file
BIN
testdata/src/test/assets/dvbsi/ait_typical.bin
vendored
Normal file
Binary file not shown.
23
testdata/src/test/assets/dvbsi/ait_typical.xml
vendored
Normal file
23
testdata/src/test/assets/dvbsi/ait_typical.xml
vendored
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<tsduck>
|
||||||
|
<AIT version="30" current="true" test_application_flag="false" application_type="0x0010">
|
||||||
|
<application control_code="0x01">
|
||||||
|
<application_identifier organization_id="0x00000120" application_id="0x0071"/>
|
||||||
|
<transport_protocol_descriptor transport_protocol_label="0x00">
|
||||||
|
<http>
|
||||||
|
<url base="http://example.com/"/>
|
||||||
|
</http>
|
||||||
|
</transport_protocol_descriptor>
|
||||||
|
<simple_application_location_descriptor initial_path="path/foo"/>
|
||||||
|
</application>
|
||||||
|
<application control_code="0x02">
|
||||||
|
<application_identifier organization_id="0x00000120" application_id="0x0072"/>
|
||||||
|
<transport_protocol_descriptor transport_protocol_label="0x00">
|
||||||
|
<http>
|
||||||
|
<url base="http://google.com/"/>
|
||||||
|
</http>
|
||||||
|
</transport_protocol_descriptor>
|
||||||
|
<simple_application_location_descriptor initial_path="path/bar"/>
|
||||||
|
</application>
|
||||||
|
</AIT>
|
||||||
|
</tsduck>
|
BIN
testdata/src/test/assets/ts/sample_ait.ts
vendored
Normal file
BIN
testdata/src/test/assets/ts/sample_ait.ts
vendored
Normal file
Binary file not shown.
146
testdata/src/test/assets/ts/sample_ait.ts.0.dump
vendored
Normal file
146
testdata/src/test/assets/ts/sample_ait.ts.0.dump
vendored
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
seekMap:
|
||||||
|
isSeekable = false
|
||||||
|
duration = UNSET TIME
|
||||||
|
getPosition(0) = [[timeUs=0, position=0]]
|
||||||
|
numberOfTracks = 2
|
||||||
|
track 330:
|
||||||
|
total output bytes = 9928
|
||||||
|
sample count = 19
|
||||||
|
format 0:
|
||||||
|
averageBitrate = -1
|
||||||
|
peakBitrate = -1
|
||||||
|
id = 1031/330
|
||||||
|
containerMimeType = null
|
||||||
|
sampleMimeType = audio/eac3
|
||||||
|
maxInputSize = -1
|
||||||
|
width = -1
|
||||||
|
height = -1
|
||||||
|
frameRate = -1.0
|
||||||
|
rotationDegrees = 0
|
||||||
|
pixelWidthHeightRatio = 1.0
|
||||||
|
channelCount = 2
|
||||||
|
sampleRate = 48000
|
||||||
|
pcmEncoding = -1
|
||||||
|
encoderDelay = 0
|
||||||
|
encoderPadding = 0
|
||||||
|
subsampleOffsetUs = 9223372036854775807
|
||||||
|
selectionFlags = 0
|
||||||
|
language = fr
|
||||||
|
drmInitData = -
|
||||||
|
metadata = null
|
||||||
|
initializationData:
|
||||||
|
sample 0:
|
||||||
|
time = 0
|
||||||
|
flags = 1
|
||||||
|
data = length 512, hash E47547D4
|
||||||
|
sample 1:
|
||||||
|
time = 32000
|
||||||
|
flags = 1
|
||||||
|
data = length 512, hash F6A537AC
|
||||||
|
sample 2:
|
||||||
|
time = 64000
|
||||||
|
flags = 1
|
||||||
|
data = length 512, hash 97391682
|
||||||
|
sample 3:
|
||||||
|
time = 96000
|
||||||
|
flags = 1
|
||||||
|
data = length 512, hash CFD3B665
|
||||||
|
sample 4:
|
||||||
|
time = 128000
|
||||||
|
flags = 1
|
||||||
|
data = length 512, hash 2E79A3AF
|
||||||
|
sample 5:
|
||||||
|
time = 160000
|
||||||
|
flags = 1
|
||||||
|
data = length 512, hash 2C24E2A3
|
||||||
|
sample 6:
|
||||||
|
time = 192000
|
||||||
|
flags = 1
|
||||||
|
data = length 512, hash 5BCB9661
|
||||||
|
sample 7:
|
||||||
|
time = 224000
|
||||||
|
flags = 1
|
||||||
|
data = length 512, hash 943ACBF2
|
||||||
|
sample 8:
|
||||||
|
time = 256000
|
||||||
|
flags = 1
|
||||||
|
data = length 512, hash B248E943
|
||||||
|
sample 9:
|
||||||
|
time = 288000
|
||||||
|
flags = 1
|
||||||
|
data = length 512, hash EC2DD86F
|
||||||
|
sample 10:
|
||||||
|
time = 320000
|
||||||
|
flags = 1
|
||||||
|
data = length 512, hash A659332F
|
||||||
|
sample 11:
|
||||||
|
time = 352000
|
||||||
|
flags = 1
|
||||||
|
data = length 512, hash CB641607
|
||||||
|
sample 12:
|
||||||
|
time = 384000
|
||||||
|
flags = 1
|
||||||
|
data = length 512, hash 157489A0
|
||||||
|
sample 13:
|
||||||
|
time = 416000
|
||||||
|
flags = 1
|
||||||
|
data = length 512, hash A37CB66E
|
||||||
|
sample 14:
|
||||||
|
time = 448000
|
||||||
|
flags = 1
|
||||||
|
data = length 512, hash 932F07D4
|
||||||
|
sample 15:
|
||||||
|
time = 480000
|
||||||
|
flags = 1
|
||||||
|
data = length 512, hash 91F50161
|
||||||
|
sample 16:
|
||||||
|
time = 512000
|
||||||
|
flags = 1
|
||||||
|
data = length 512, hash 7F9D6CCB
|
||||||
|
sample 17:
|
||||||
|
time = 544000
|
||||||
|
flags = 1
|
||||||
|
data = length 512, hash 3955F015
|
||||||
|
sample 18:
|
||||||
|
time = 576000
|
||||||
|
flags = 1
|
||||||
|
data = length 512, hash A8E5C938
|
||||||
|
track 370:
|
||||||
|
total output bytes = 1413
|
||||||
|
sample count = 3
|
||||||
|
format 0:
|
||||||
|
averageBitrate = -1
|
||||||
|
peakBitrate = -1
|
||||||
|
id = null
|
||||||
|
containerMimeType = null
|
||||||
|
sampleMimeType = application/vnd.dvb.ait
|
||||||
|
maxInputSize = -1
|
||||||
|
width = -1
|
||||||
|
height = -1
|
||||||
|
frameRate = -1.0
|
||||||
|
rotationDegrees = 0
|
||||||
|
pixelWidthHeightRatio = 1.0
|
||||||
|
channelCount = -1
|
||||||
|
sampleRate = -1
|
||||||
|
pcmEncoding = -1
|
||||||
|
encoderDelay = 0
|
||||||
|
encoderPadding = 0
|
||||||
|
subsampleOffsetUs = -43622564033
|
||||||
|
selectionFlags = 0
|
||||||
|
language = null
|
||||||
|
drmInitData = -
|
||||||
|
metadata = null
|
||||||
|
initializationData:
|
||||||
|
sample 0:
|
||||||
|
time = 0
|
||||||
|
flags = 1
|
||||||
|
data = length 471, hash B189052F
|
||||||
|
sample 1:
|
||||||
|
time = 192000
|
||||||
|
flags = 1
|
||||||
|
data = length 471, hash B189052F
|
||||||
|
sample 2:
|
||||||
|
time = 384000
|
||||||
|
flags = 1
|
||||||
|
data = length 471, hash B189052F
|
||||||
|
tracksEnded = true
|
146
testdata/src/test/assets/ts/sample_ait.ts.unklen.dump
vendored
Normal file
146
testdata/src/test/assets/ts/sample_ait.ts.unklen.dump
vendored
Normal file
@ -0,0 +1,146 @@
|
|||||||
|
seekMap:
|
||||||
|
isSeekable = false
|
||||||
|
duration = UNSET TIME
|
||||||
|
getPosition(0) = [[timeUs=0, position=0]]
|
||||||
|
numberOfTracks = 2
|
||||||
|
track 330:
|
||||||
|
total output bytes = 9928
|
||||||
|
sample count = 19
|
||||||
|
format 0:
|
||||||
|
averageBitrate = -1
|
||||||
|
peakBitrate = -1
|
||||||
|
id = 1031/330
|
||||||
|
containerMimeType = null
|
||||||
|
sampleMimeType = audio/eac3
|
||||||
|
maxInputSize = -1
|
||||||
|
width = -1
|
||||||
|
height = -1
|
||||||
|
frameRate = -1.0
|
||||||
|
rotationDegrees = 0
|
||||||
|
pixelWidthHeightRatio = 1.0
|
||||||
|
channelCount = 2
|
||||||
|
sampleRate = 48000
|
||||||
|
pcmEncoding = -1
|
||||||
|
encoderDelay = 0
|
||||||
|
encoderPadding = 0
|
||||||
|
subsampleOffsetUs = 9223372036854775807
|
||||||
|
selectionFlags = 0
|
||||||
|
language = fr
|
||||||
|
drmInitData = -
|
||||||
|
metadata = null
|
||||||
|
initializationData:
|
||||||
|
sample 0:
|
||||||
|
time = 0
|
||||||
|
flags = 1
|
||||||
|
data = length 512, hash E47547D4
|
||||||
|
sample 1:
|
||||||
|
time = 32000
|
||||||
|
flags = 1
|
||||||
|
data = length 512, hash F6A537AC
|
||||||
|
sample 2:
|
||||||
|
time = 64000
|
||||||
|
flags = 1
|
||||||
|
data = length 512, hash 97391682
|
||||||
|
sample 3:
|
||||||
|
time = 96000
|
||||||
|
flags = 1
|
||||||
|
data = length 512, hash CFD3B665
|
||||||
|
sample 4:
|
||||||
|
time = 128000
|
||||||
|
flags = 1
|
||||||
|
data = length 512, hash 2E79A3AF
|
||||||
|
sample 5:
|
||||||
|
time = 160000
|
||||||
|
flags = 1
|
||||||
|
data = length 512, hash 2C24E2A3
|
||||||
|
sample 6:
|
||||||
|
time = 192000
|
||||||
|
flags = 1
|
||||||
|
data = length 512, hash 5BCB9661
|
||||||
|
sample 7:
|
||||||
|
time = 224000
|
||||||
|
flags = 1
|
||||||
|
data = length 512, hash 943ACBF2
|
||||||
|
sample 8:
|
||||||
|
time = 256000
|
||||||
|
flags = 1
|
||||||
|
data = length 512, hash B248E943
|
||||||
|
sample 9:
|
||||||
|
time = 288000
|
||||||
|
flags = 1
|
||||||
|
data = length 512, hash EC2DD86F
|
||||||
|
sample 10:
|
||||||
|
time = 320000
|
||||||
|
flags = 1
|
||||||
|
data = length 512, hash A659332F
|
||||||
|
sample 11:
|
||||||
|
time = 352000
|
||||||
|
flags = 1
|
||||||
|
data = length 512, hash CB641607
|
||||||
|
sample 12:
|
||||||
|
time = 384000
|
||||||
|
flags = 1
|
||||||
|
data = length 512, hash 157489A0
|
||||||
|
sample 13:
|
||||||
|
time = 416000
|
||||||
|
flags = 1
|
||||||
|
data = length 512, hash A37CB66E
|
||||||
|
sample 14:
|
||||||
|
time = 448000
|
||||||
|
flags = 1
|
||||||
|
data = length 512, hash 932F07D4
|
||||||
|
sample 15:
|
||||||
|
time = 480000
|
||||||
|
flags = 1
|
||||||
|
data = length 512, hash 91F50161
|
||||||
|
sample 16:
|
||||||
|
time = 512000
|
||||||
|
flags = 1
|
||||||
|
data = length 512, hash 7F9D6CCB
|
||||||
|
sample 17:
|
||||||
|
time = 544000
|
||||||
|
flags = 1
|
||||||
|
data = length 512, hash 3955F015
|
||||||
|
sample 18:
|
||||||
|
time = 576000
|
||||||
|
flags = 1
|
||||||
|
data = length 512, hash A8E5C938
|
||||||
|
track 370:
|
||||||
|
total output bytes = 1413
|
||||||
|
sample count = 3
|
||||||
|
format 0:
|
||||||
|
averageBitrate = -1
|
||||||
|
peakBitrate = -1
|
||||||
|
id = null
|
||||||
|
containerMimeType = null
|
||||||
|
sampleMimeType = application/vnd.dvb.ait
|
||||||
|
maxInputSize = -1
|
||||||
|
width = -1
|
||||||
|
height = -1
|
||||||
|
frameRate = -1.0
|
||||||
|
rotationDegrees = 0
|
||||||
|
pixelWidthHeightRatio = 1.0
|
||||||
|
channelCount = -1
|
||||||
|
sampleRate = -1
|
||||||
|
pcmEncoding = -1
|
||||||
|
encoderDelay = 0
|
||||||
|
encoderPadding = 0
|
||||||
|
subsampleOffsetUs = -43622564033
|
||||||
|
selectionFlags = 0
|
||||||
|
language = null
|
||||||
|
drmInitData = -
|
||||||
|
metadata = null
|
||||||
|
initializationData:
|
||||||
|
sample 0:
|
||||||
|
time = 0
|
||||||
|
flags = 1
|
||||||
|
data = length 471, hash B189052F
|
||||||
|
sample 1:
|
||||||
|
time = 192000
|
||||||
|
flags = 1
|
||||||
|
data = length 471, hash B189052F
|
||||||
|
sample 2:
|
||||||
|
time = 384000
|
||||||
|
flags = 1
|
||||||
|
data = length 471, hash B189052F
|
||||||
|
tracksEnded = true
|
Loading…
x
Reference in New Issue
Block a user