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
|
||||
show how to render video to a `GLSurfaceView` while applying a GL shader.
|
||||
([#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) ###
|
||||
|
||||
|
@ -97,6 +97,7 @@ public final class MimeTypes {
|
||||
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_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<>();
|
||||
|
||||
|
@ -15,6 +15,9 @@
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
@ -277,6 +280,31 @@ public final class ParsableBitArray {
|
||||
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
|
||||
* 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;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
import static org.junit.Assert.assertThrows;
|
||||
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||
import com.google.android.exoplayer2.C;
|
||||
import com.google.android.exoplayer2.testutil.TestUtil;
|
||||
import java.nio.charset.Charset;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@ -276,6 +279,35 @@ public final class ParsableBitArrayTest {
|
||||
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
|
||||
public void testPutBitsWithinByte() {
|
||||
ParsableBitArray output = new ParsableBitArray(new byte[4]);
|
||||
|
@ -17,6 +17,7 @@ package com.google.android.exoplayer2.metadata;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
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.icy.IcyDecoder;
|
||||
import com.google.android.exoplayer2.metadata.id3.Id3Decoder;
|
||||
@ -67,7 +68,8 @@ public interface MetadataDecoderFactory {
|
||||
return MimeTypes.APPLICATION_ID3.equals(mimeType)
|
||||
|| MimeTypes.APPLICATION_EMSG.equals(mimeType)
|
||||
|| MimeTypes.APPLICATION_SCTE35.equals(mimeType)
|
||||
|| MimeTypes.APPLICATION_ICY.equals(mimeType);
|
||||
|| MimeTypes.APPLICATION_ICY.equals(mimeType)
|
||||
|| MimeTypes.APPLICATION_AIT.equals(mimeType);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -83,6 +85,8 @@ public interface MetadataDecoderFactory {
|
||||
return new SpliceInfoDecoder();
|
||||
case MimeTypes.APPLICATION_ICY:
|
||||
return new IcyDecoder();
|
||||
case MimeTypes.APPLICATION_AIT:
|
||||
return new AppInfoTableDecoder();
|
||||
default:
|
||||
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).
|
||||
*/
|
||||
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;
|
||||
/**
|
||||
* 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)));
|
||||
case TsExtractor.TS_STREAM_TYPE_SPLICE_INFO:
|
||||
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:
|
||||
return new PesReader(new Id3Reader());
|
||||
case TsExtractor.TS_STREAM_TYPE_DVBSUBS:
|
||||
return new PesReader(
|
||||
new DvbSubtitleReader(esInfo.dvbSubtitleInfos));
|
||||
case TsExtractor.TS_STREAM_TYPE_AIT:
|
||||
return new SectionReader(new PassthroughSectionPayloadReader(MimeTypes.APPLICATION_AIT));
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If {@link #FLAG_OVERRIDE_CAPTION_DESCRIPTORS} is set, returns a {@link SeiReader} for
|
||||
* {@link #closedCaptionFormats}. If unset, parses the PMT descriptor information and returns a
|
||||
* {@link SeiReader} for the declared formats, or {@link #closedCaptionFormats} if the descriptor
|
||||
* is not present.
|
||||
* If {@link #FLAG_OVERRIDE_CAPTION_DESCRIPTORS} is set, returns a {@link SeiReader} for {@link
|
||||
* #closedCaptionFormats}. If unset, parses the PMT descriptor information and returns a {@link
|
||||
* SeiReader} for the declared formats, or {@link #closedCaptionFormats} if the descriptor is not
|
||||
* present.
|
||||
*
|
||||
* @param esInfo The {@link EsInfo} passed to {@link #createPayloadReader(int, EsInfo)}.
|
||||
* @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.TrackOutput;
|
||||
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.TimestampAdjuster;
|
||||
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;
|
||||
|
||||
/**
|
||||
* 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 TrackOutput output;
|
||||
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
|
||||
public void init(TimestampAdjuster timestampAdjuster, ExtractorOutput extractorOutput,
|
||||
public void init(
|
||||
TimestampAdjuster timestampAdjuster,
|
||||
ExtractorOutput extractorOutput,
|
||||
TsPayloadReader.TrackIdGenerator idGenerator) {
|
||||
this.timestampAdjuster = timestampAdjuster;
|
||||
idGenerator.generateNewId();
|
||||
@ -53,14 +66,20 @@ public final class SpliceInfoSectionReader implements SectionPayloadReader {
|
||||
return;
|
||||
}
|
||||
output.format(
|
||||
Format.createSampleFormat(null, MimeTypes.APPLICATION_SCTE35)
|
||||
.copyWithSubsampleOffsetUs(timestampAdjuster.getTimestampOffsetUs()));
|
||||
new Format.Builder()
|
||||
.setSampleMimeType(mimeType)
|
||||
.setSubsampleOffsetUs(timestampAdjuster.getTimestampOffsetUs())
|
||||
.build());
|
||||
formatDeclared = true;
|
||||
}
|
||||
int sampleSize = sectionData.bytesLeft();
|
||||
output.sampleData(sectionData, sampleSize);
|
||||
output.sampleMetadata(timestampAdjuster.getLastAdjustedTimestampUs(), C.BUFFER_FLAG_KEY_FRAME,
|
||||
sampleSize, 0, null);
|
||||
output.sampleMetadata(
|
||||
timestampAdjuster.getLastAdjustedTimestampUs(),
|
||||
C.BUFFER_FLAG_KEY_FRAME,
|
||||
sampleSize,
|
||||
0,
|
||||
null);
|
||||
}
|
||||
|
||||
@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_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_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_ISO639_LANG = 0x0A;
|
||||
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_DTS = 0x7B;
|
||||
private static final int TS_PMT_DESC_DVB_EXT = 0x7F;
|
||||
@ -578,7 +582,7 @@ public final class TsExtractor implements Extractor {
|
||||
pmtScratch.skipBits(4); // reserved
|
||||
int esInfoLength = pmtScratch.readBits(12); // ES_info_length.
|
||||
EsInfo esInfo = readEsInfo(sectionData, esInfoLength);
|
||||
if (streamType == 0x06) {
|
||||
if (streamType == 0x06 || streamType == 0x05) {
|
||||
streamType = esInfo.streamType;
|
||||
}
|
||||
remainingEntriesLength -= esInfoLength + 5;
|
||||
@ -688,6 +692,8 @@ public final class TsExtractor implements Extractor {
|
||||
dvbSubtitleInfos.add(new DvbSubtitleInfo(dvbLanguage, dvbSubtitlingType,
|
||||
initializationData));
|
||||
}
|
||||
} else if (descriptorTag == TS_PMT_DESC_AIT) {
|
||||
streamType = TS_STREAM_TYPE_AIT;
|
||||
}
|
||||
// Skip unused bytes of current descriptor.
|
||||
data.skipBytes(positionOfNextDescriptor - data.getPosition());
|
||||
|
@ -53,6 +53,11 @@ public final class TsExtractorTest {
|
||||
ExtractorAsserts.assertBehavior(TsExtractor::new, "ts/sample_scte35.ts");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAit() throws Exception {
|
||||
ExtractorAsserts.assertBehavior(TsExtractor::new, "ts/sample_ait.ts");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStreamWithJunkData() throws Exception {
|
||||
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