diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 4db085b9e1..cfa9686a7e 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -1,11 +1,48 @@ # Release notes +### 2.12.3 (2021-01-13) ### + +* Core library: + * Fix `MediaCodecRenderer` issue where empty streams would fail to play in + bypass mode ([#8374](https://github.com/google/ExoPlayer/issues/8374)). + * Fix playback issues after seeking during an ad + ([#8349](https://github.com/google/ExoPlayer/issues/8349)). + * Fix propagation of `LoadErrorHandlingPolicy` from + `DefaultMediaSourceFactory` into `SingleSampleMediaSource.Factory` when + creating subtitle media sources from + `MediaItem.playbackProperties.subtitles` + ([#8430](https://github.com/google/ExoPlayer/issues/8430)). +* UI: + * Fix issue where pop-up menus belonging to `StyledPlayerControlView` + would not be dismissed when tapping outside of the menu area or pressing + the back button, on API level 22 and earlier + ([#8272](https://github.com/google/ExoPlayer/issues/8272)). +* Downloads: + * Fix crash in `DownloadManager` that could occur when adding a stopped + download with the same ID as a download currently being removed + ([#8419](https://github.com/google/ExoPlayer/issues/8419)). +* Text: + * Gracefully handle null-terminated subtitle content in Matroska + containers. + * Fix CEA-708 anchor positioning + ([#1807](https://github.com/google/ExoPlayer/issues/1807)). +* IMA extension: + * Fix a condition where playback could get stuck before an empty ad + ([#8205](https://github.com/google/ExoPlayer/issues/8205)). + * Log a warning rather than throwing when reaching the end of the stream + with an ad playing but without ad media info + ([#8290](https://github.com/google/ExoPlayer/issues/8290)). +* Media2 extension: + * Make media2-extension depend on AndroidX media2:media2-session:1.1.0 to + fix a deadlock while creating PlaybackStateCompat internally. + ([#8011](https://github.com/google/ExoPlayer/issues/8011)). + ### 2.12.2 (2020-12-01) ### * Core library: - * Suppress exceptions from registering/unregistering the stream volume - receiver ([#8087](https://github.com/google/ExoPlayer/issues/8087)), - ([#8106](https://github.com/google/ExoPlayer/issues/8106)). + * Suppress exceptions from registering and unregistering the stream volume + receiver ([#8087](https://github.com/google/ExoPlayer/issues/8087), + [#8106](https://github.com/google/ExoPlayer/issues/8106)). * Suppress ProGuard warnings caused by Guava's compile-only dependencies ([#8103](https://github.com/google/ExoPlayer/issues/8103)). * Fix issue that could cause playback to freeze when selecting tracks, if @@ -58,8 +95,9 @@ that lie outside the length of the cue text. * Media2 extension: * Notify onBufferingEnded when the state of origin player becomes - STATE_IDLE or STATE_ENDED. - * Allow to remove all playlist items that makes the player reset. + `STATE_IDLE` or `STATE_ENDED`. + * Allow to remove all playlist items that makes the player reset + ([#8047](https://github.com/google/ExoPlayer/issues/8047)). ### 2.12.1 (2020-10-23) ### @@ -118,7 +156,7 @@ ([#7378](https://github.com/google/ExoPlayer/issues/7378)). * Downloads: Fix issue retrying progressive downloads, which could also result in a crash in `DownloadManager.InternalHandler.onContentLengthChanged` - ([#8078](https://github.com/google/ExoPlayer/issues/8078). + ([#8078](https://github.com/google/ExoPlayer/issues/8078)). * HLS: Fix crash affecting chunkful preparation of master playlists that start with an I-FRAME only variant ([#8025](https://github.com/google/ExoPlayer/issues/8025)). diff --git a/constants.gradle b/constants.gradle index b06636dd20..50ce661b9a 100644 --- a/constants.gradle +++ b/constants.gradle @@ -13,8 +13,8 @@ // limitations under the License. project.ext { // ExoPlayer version and version code. - releaseVersion = '2.12.2' - releaseVersionCode = 2012002 + releaseVersion = '2.12.3' + releaseVersionCode = 2012003 minSdkVersion = 16 appTargetSdkVersion = 29 targetSdkVersion = 28 // TODO: Bump once b/143232359 is resolved. Also fix TODOs in UtilTest. @@ -32,7 +32,9 @@ project.ext { androidxAnnotationVersion = '1.1.0' androidxAppCompatVersion = '1.1.0' androidxCollectionVersion = '1.1.0' + androidxFuturesVersion = '1.1.0' androidxMediaVersion = '1.0.1' + androidxMedia2Version = '1.1.0' androidxMultidexVersion = '2.0.0' androidxRecyclerViewVersion = '1.1.0' androidxTestCoreVersion = '1.2.0' diff --git a/demos/main/src/main/assets/media.exolist.json b/demos/main/src/main/assets/media.exolist.json index baa89f11ff..4d9e1df23a 100644 --- a/demos/main/src/main/assets/media.exolist.json +++ b/demos/main/src/main/assets/media.exolist.json @@ -251,7 +251,9 @@ { "name": "Super speed (MP4, H264, PlayReady)", "uri": "https://playready.directtaps.net/smoothstreaming/SSWSS720H264PR/SuperSpeedway_720.ism/Manifest", - "drm_scheme": "playready" + "drm_scheme": "playready", + "drm_license_uri": "https://playready.directtaps.net/pr/svc/rightsmanager.asmx", + "drm_force_default_license_uri": true } ] }, diff --git a/extensions/gvr/src/main/java/com/google/android/exoplayer2/ext/gvr/GvrAudioProcessor.java b/extensions/gvr/src/main/java/com/google/android/exoplayer2/ext/gvr/GvrAudioProcessor.java index 14570759c3..041ca09db4 100644 --- a/extensions/gvr/src/main/java/com/google/android/exoplayer2/ext/gvr/GvrAudioProcessor.java +++ b/extensions/gvr/src/main/java/com/google/android/exoplayer2/ext/gvr/GvrAudioProcessor.java @@ -135,6 +135,7 @@ public class GvrAudioProcessor implements AudioProcessor { @Override public void queueEndOfStream() { + // TODO(internal b/174554082): assert gvrAudioSurround is non-null here and in getOutput. if (gvrAudioSurround != null) { gvrAudioSurround.triggerProcessing(); } diff --git a/extensions/ima/src/main/java/com/google/android/exoplayer2/ext/ima/ImaAdsLoader.java b/extensions/ima/src/main/java/com/google/android/exoplayer2/ext/ima/ImaAdsLoader.java index 252aa2e396..574fbfa917 100644 --- a/extensions/ima/src/main/java/com/google/android/exoplayer2/ext/ima/ImaAdsLoader.java +++ b/extensions/ima/src/main/java/com/google/android/exoplayer2/ext/ima/ImaAdsLoader.java @@ -1260,7 +1260,7 @@ public final class ImaAdsLoader && playWhenReady) { ensureSentContentCompleteIfAtEndOfStream(); } else if (imaAdState != IMA_AD_STATE_NONE && playbackState == Player.STATE_ENDED) { - AdMediaInfo adMediaInfo = checkNotNull(imaAdMediaInfo); + @Nullable AdMediaInfo adMediaInfo = imaAdMediaInfo; if (adMediaInfo == null) { Log.w(TAG, "onEnded without ad media info"); } else { diff --git a/extensions/media2/build.gradle b/extensions/media2/build.gradle index bdafee5558..a89354d7b3 100644 --- a/extensions/media2/build.gradle +++ b/extensions/media2/build.gradle @@ -18,8 +18,8 @@ android.defaultConfig.minSdkVersion 19 dependencies { implementation project(modulePrefix + 'library-core') implementation 'androidx.collection:collection:' + androidxCollectionVersion - implementation 'androidx.concurrent:concurrent-futures:1.1.0' - api 'androidx.media2:media2-session:1.0.3' + implementation 'androidx.concurrent:concurrent-futures:' + androidxFuturesVersion + api 'androidx.media2:media2-session:' + androidxMedia2Version compileOnly 'org.checkerframework:checker-compat-qual:' + checkerframeworkCompatVersion compileOnly 'org.checkerframework:checker-qual:' + checkerframeworkVersion compileOnly 'org.jetbrains.kotlin:kotlin-annotations-jvm:' + kotlinAnnotationsVersion diff --git a/extensions/media2/src/androidTest/java/com/google/android/exoplayer2/ext/media2/MediaSessionUtilTest.java b/extensions/media2/src/androidTest/java/com/google/android/exoplayer2/ext/media2/MediaSessionUtilTest.java deleted file mode 100644 index 8cf586b846..0000000000 --- a/extensions/media2/src/androidTest/java/com/google/android/exoplayer2/ext/media2/MediaSessionUtilTest.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright 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.ext.media2; - -import static com.google.common.truth.Truth.assertThat; -import static java.util.concurrent.TimeUnit.MILLISECONDS; - -import android.content.Context; -import android.support.v4.media.session.MediaControllerCompat; -import android.support.v4.media.session.MediaSessionCompat; -import androidx.annotation.NonNull; -import androidx.media2.common.SessionPlayer; -import androidx.media2.common.SessionPlayer.PlayerResult; -import androidx.media2.session.MediaSession; -import androidx.test.core.app.ApplicationProvider; -import androidx.test.ext.junit.runners.AndroidJUnit4; -import androidx.test.platform.app.InstrumentationRegistry; -import com.google.android.exoplayer2.ext.media2.test.R; -import com.google.android.exoplayer2.util.Assertions; -import com.google.common.util.concurrent.ListenableFuture; -import java.util.concurrent.CountDownLatch; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; - -/** Unit test for {@link MediaSessionUtil} */ -@RunWith(AndroidJUnit4.class) -public class MediaSessionUtilTest { - private static final int PLAYER_STATE_CHANGE_WAIT_TIME_MS = 5_000; - - @Rule public final PlayerTestRule playerTestRule = new PlayerTestRule(); - - @Test - public void getSessionCompatToken_withMediaControllerCompat_returnsValidToken() throws Exception { - Context context = ApplicationProvider.getApplicationContext(); - - SessionPlayerConnector sessionPlayerConnector = playerTestRule.getSessionPlayerConnector(); - MediaSession.SessionCallback sessionCallback = - new SessionCallbackBuilder(context, sessionPlayerConnector).build(); - TestUtils.loadResource(R.raw.audio, sessionPlayerConnector); - ListenableFuture prepareResult = sessionPlayerConnector.prepare(); - CountDownLatch latch = new CountDownLatch(1); - sessionPlayerConnector.registerPlayerCallback( - playerTestRule.getExecutor(), - new SessionPlayer.PlayerCallback() { - @Override - public void onPlayerStateChanged(@NonNull SessionPlayer player, int playerState) { - if (playerState == SessionPlayer.PLAYER_STATE_PLAYING) { - latch.countDown(); - } - } - }); - - MediaSession session2 = - new MediaSession.Builder(context, sessionPlayerConnector) - .setSessionCallback(playerTestRule.getExecutor(), sessionCallback) - .build(); - - InstrumentationRegistry.getInstrumentation() - .runOnMainSync( - () -> { - try { - MediaSessionCompat.Token token = - Assertions.checkNotNull(MediaSessionUtil.getSessionCompatToken(session2)); - MediaControllerCompat controllerCompat = new MediaControllerCompat(context, token); - controllerCompat.getTransportControls().play(); - } catch (Exception e) { - throw new IllegalStateException(e); - } - }); - assertThat(prepareResult.get(PLAYER_STATE_CHANGE_WAIT_TIME_MS, MILLISECONDS).getResultCode()) - .isEqualTo(PlayerResult.RESULT_SUCCESS); - assertThat(latch.await(PLAYER_STATE_CHANGE_WAIT_TIME_MS, MILLISECONDS)).isTrue(); - } -} diff --git a/extensions/media2/src/main/java/com/google/android/exoplayer2/ext/media2/MediaSessionUtil.java b/extensions/media2/src/main/java/com/google/android/exoplayer2/ext/media2/MediaSessionUtil.java deleted file mode 100644 index e7cc9545b1..0000000000 --- a/extensions/media2/src/main/java/com/google/android/exoplayer2/ext/media2/MediaSessionUtil.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 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.ext.media2; - -import android.annotation.SuppressLint; -import android.support.v4.media.session.MediaSessionCompat; -import androidx.media2.session.MediaSession; - -/** Utility methods to use {@link MediaSession} with other ExoPlayer modules. */ -public final class MediaSessionUtil { - - /** Gets the {@link MediaSessionCompat.Token} from the {@link MediaSession}. */ - // TODO(b/152764014): Deprecate this API when MediaSession#getSessionCompatToken() is released. - public static MediaSessionCompat.Token getSessionCompatToken(MediaSession mediaSession) { - @SuppressLint("RestrictedApi") - @SuppressWarnings("RestrictTo") - MediaSessionCompat sessionCompat = mediaSession.getSessionCompat(); - return sessionCompat.getSessionToken(); - } - - private MediaSessionUtil() { - // Prevent from instantiation. - } -} diff --git a/extensions/media2/src/main/java/com/google/android/exoplayer2/ext/media2/SessionPlayerConnector.java b/extensions/media2/src/main/java/com/google/android/exoplayer2/ext/media2/SessionPlayerConnector.java index ccb29f8ca6..0415a5cf38 100644 --- a/extensions/media2/src/main/java/com/google/android/exoplayer2/ext/media2/SessionPlayerConnector.java +++ b/extensions/media2/src/main/java/com/google/android/exoplayer2/ext/media2/SessionPlayerConnector.java @@ -437,8 +437,6 @@ public final class SessionPlayerConnector extends SessionPlayer { /* defaultValueWhenException= */ END_OF_PLAYLIST); } - // TODO(b/147706139): Call super.close() after updating media2-common to 1.1.0 - @SuppressWarnings("MissingSuperCall") @Override public void close() { synchronized (stateLock) { @@ -454,6 +452,7 @@ public final class SessionPlayerConnector extends SessionPlayer { player.close(); return null; }); + super.close(); } // SessionPlayerConnector-specific functions. @@ -559,8 +558,8 @@ public final class SessionPlayerConnector extends SessionPlayer { } } - // TODO: Remove this suppress warnings and call onCurrentMediaItemChanged with a null item - // once AndroidX media2 1.2.0 is released + // TODO(internal b/160846312): Remove this suppress warnings and call onCurrentMediaItemChanged + // with a null item once we depend on media2 1.2.0. @SuppressWarnings("nullness:argument.type.incompatible") private void handlePlaylistChangedOnHandler() { List currentPlaylist = player.getPlaylist(); @@ -578,11 +577,6 @@ public final class SessionPlayerConnector extends SessionPlayer { SessionPlayerConnector.this, currentPlaylist, playlistMetadata); if (notifyCurrentMediaItem) { callback.onCurrentMediaItemChanged(SessionPlayerConnector.this, currentMediaItem); - - // Workaround for MediaSession's issue that current media item change isn't propagated - // to the legacy controllers. - // TODO(b/160846312): Remove this workaround with media2 1.1.0-stable. - callback.onSeekCompleted(SessionPlayerConnector.this, currentPosition); } }); } @@ -597,11 +591,6 @@ public final class SessionPlayerConnector extends SessionPlayer { notifySessionPlayerCallback( callback -> { callback.onCurrentMediaItemChanged(SessionPlayerConnector.this, currentMediaItem); - - // Workaround for MediaSession's issue that current media item change isn't propagated - // to the legacy controllers. - // TODO(b/160846312): Remove this workaround with media2 1.1.0-stable. - callback.onSeekCompleted(SessionPlayerConnector.this, currentPosition); }); } @@ -722,11 +711,6 @@ public final class SessionPlayerConnector extends SessionPlayer { notifySessionPlayerCallback( callback -> { callback.onCurrentMediaItemChanged(SessionPlayerConnector.this, mediaItem); - - // Workaround for MediaSession's issue that current media item change isn't propagated - // to the legacy controllers. - // TODO(b/160846312): Remove this workaround with media2 1.1.0-stable. - callback.onSeekCompleted(SessionPlayerConnector.this, currentPosition); }); } diff --git a/library/common/src/main/java/com/google/android/exoplayer2/ExoPlayerLibraryInfo.java b/library/common/src/main/java/com/google/android/exoplayer2/ExoPlayerLibraryInfo.java index 0caa3d50df..aadf3bd0a4 100644 --- a/library/common/src/main/java/com/google/android/exoplayer2/ExoPlayerLibraryInfo.java +++ b/library/common/src/main/java/com/google/android/exoplayer2/ExoPlayerLibraryInfo.java @@ -30,11 +30,11 @@ public final class ExoPlayerLibraryInfo { /** The version of the library expressed as a string, for example "1.2.3". */ // Intentionally hardcoded. Do not derive from other constants (e.g. VERSION_INT) or vice versa. - public static final String VERSION = "2.12.2"; + public static final String VERSION = "2.12.3"; /** The version of the library expressed as {@code "ExoPlayerLib/" + VERSION}. */ // Intentionally hardcoded. Do not derive from other constants (e.g. VERSION) or vice versa. - public static final String VERSION_SLASHY = "ExoPlayerLib/2.12.2"; + public static final String VERSION_SLASHY = "ExoPlayerLib/2.12.3"; /** * The version of the library expressed as an integer, for example 1002003. @@ -44,7 +44,7 @@ public final class ExoPlayerLibraryInfo { * integer version 123045006 (123-045-006). */ // Intentionally hardcoded. Do not derive from other constants (e.g. VERSION) or vice versa. - public static final int VERSION_INT = 2012002; + public static final int VERSION_INT = 2012003; /** The default user agent for requests made by the library. */ public static final String DEFAULT_USER_AGENT = diff --git a/library/common/src/main/java/com/google/android/exoplayer2/audio/AacUtil.java b/library/common/src/main/java/com/google/android/exoplayer2/audio/AacUtil.java index 4a03b79856..c97893b428 100644 --- a/library/common/src/main/java/com/google/android/exoplayer2/audio/AacUtil.java +++ b/library/common/src/main/java/com/google/android/exoplayer2/audio/AacUtil.java @@ -18,7 +18,6 @@ package com.google.android.exoplayer2.audio; import androidx.annotation.IntDef; import com.google.android.exoplayer2.C; import com.google.android.exoplayer2.ParserException; -import com.google.android.exoplayer2.util.Assertions; import com.google.android.exoplayer2.util.Log; import com.google.android.exoplayer2.util.ParsableBitArray; import java.lang.annotation.Documented; @@ -173,7 +172,8 @@ public final class AacUtil { * * @param audioSpecificConfig A byte array containing the AudioSpecificConfig to parse. * @return The parsed configuration. - * @throws ParserException If the AudioSpecificConfig cannot be parsed as it's not supported. + * @throws ParserException If the AudioSpecificConfig cannot be parsed because it is invalid or + * unsupported. */ public static Config parseAudioSpecificConfig(byte[] audioSpecificConfig) throws ParserException { return parseAudioSpecificConfig( @@ -188,7 +188,8 @@ public final class AacUtil { * @param forceReadToEnd Whether the entire AudioSpecificConfig should be read. Required for * knowing the length of the configuration payload. * @return The parsed configuration. - * @throws ParserException If the AudioSpecificConfig cannot be parsed as it's not supported. + * @throws ParserException If the AudioSpecificConfig cannot be parsed because it is invalid or + * unsupported. */ public static Config parseAudioSpecificConfig(ParsableBitArray bitArray, boolean forceReadToEnd) throws ParserException { @@ -248,7 +249,9 @@ public final class AacUtil { } // For supported containers, bits_to_decode() is always 0. int channelCount = AUDIO_SPECIFIC_CONFIG_CHANNEL_COUNT_TABLE[channelConfiguration]; - Assertions.checkArgument(channelCount != AUDIO_SPECIFIC_CONFIG_CHANNEL_CONFIGURATION_INVALID); + if (channelCount == AUDIO_SPECIFIC_CONFIG_CHANNEL_CONFIGURATION_INVALID) { + throw new ParserException(); + } return new Config(sampleRateHz, channelCount, codecs); } @@ -336,15 +339,17 @@ public final class AacUtil { * * @param bitArray The bit array containing the audio specific configuration. * @return The sampling frequency. + * @throws ParserException If the audio specific configuration is invalid. */ - private static int getSamplingFrequency(ParsableBitArray bitArray) { + private static int getSamplingFrequency(ParsableBitArray bitArray) throws ParserException { int samplingFrequency; int frequencyIndex = bitArray.readBits(4); if (frequencyIndex == AUDIO_SPECIFIC_CONFIG_FREQUENCY_INDEX_ARBITRARY) { samplingFrequency = bitArray.readBits(24); - } else { - Assertions.checkArgument(frequencyIndex < 13); + } else if (frequencyIndex < 13) { samplingFrequency = AUDIO_SPECIFIC_CONFIG_SAMPLING_RATE_TABLE[frequencyIndex]; + } else { + throw new ParserException(); } return samplingFrequency; } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java index 377863a083..17463b3cd1 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImpl.java @@ -601,8 +601,10 @@ import java.util.concurrent.TimeoutException; // general because the midroll ad preceding the seek destination must be played before the // content position can be played, if a different ad is playing at the moment. Log.w(TAG, "seekTo ignored because an ad is playing"); - playbackInfoUpdateListener.onPlaybackInfoUpdate( - new ExoPlayerImplInternal.PlaybackInfoUpdate(playbackInfo)); + ExoPlayerImplInternal.PlaybackInfoUpdate playbackInfoUpdate = + new ExoPlayerImplInternal.PlaybackInfoUpdate(this.playbackInfo); + playbackInfoUpdate.incrementPendingOperationAcks(1); + playbackInfoUpdateListener.onPlaybackInfoUpdate(playbackInfoUpdate); return; } @Player.State diff --git a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java index e33b93ac0e..38d57ac0bc 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/ExoPlayerImplInternal.java @@ -1762,8 +1762,8 @@ import java.util.concurrent.atomic.AtomicBoolean; } } if (shouldContinueLoading) { - // We should still be loading, except in the case that it's no longer possible (i.e., because - // we've loaded the current playlist to the end). + // We should still be loading, except when there is nothing to load or we have fully loaded + // the current period. shouldContinueLoading = isLoadingPossible(); updateIsLoading(); } else { diff --git a/library/core/src/main/java/com/google/android/exoplayer2/MediaPeriodQueue.java b/library/core/src/main/java/com/google/android/exoplayer2/MediaPeriodQueue.java index fa6201bf37..7ba7589a56 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/MediaPeriodQueue.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/MediaPeriodQueue.java @@ -672,15 +672,13 @@ import com.google.common.collect.ImmutableList; period.getNextAdIndexToPlay(adGroupIndex, currentPeriodId.adIndexInAdGroup); if (nextAdIndexInAdGroup < adCountInCurrentAdGroup) { // Play the next ad in the ad group if it's available. - return !period.isAdAvailable(adGroupIndex, nextAdIndexInAdGroup) - ? null - : getMediaPeriodInfoForAd( - timeline, - currentPeriodId.periodUid, - adGroupIndex, - nextAdIndexInAdGroup, - mediaPeriodInfo.requestedContentPositionUs, - currentPeriodId.windowSequenceNumber); + return getMediaPeriodInfoForAd( + timeline, + currentPeriodId.periodUid, + adGroupIndex, + nextAdIndexInAdGroup, + mediaPeriodInfo.requestedContentPositionUs, + currentPeriodId.windowSequenceNumber); } else { // Play content from the ad group position. long startPositionUs = mediaPeriodInfo.requestedContentPositionUs; @@ -720,15 +718,13 @@ import com.google.common.collect.ImmutableList; currentPeriodId.windowSequenceNumber); } int adIndexInAdGroup = period.getFirstAdIndexToPlay(nextAdGroupIndex); - return !period.isAdAvailable(nextAdGroupIndex, adIndexInAdGroup) - ? null - : getMediaPeriodInfoForAd( - timeline, - currentPeriodId.periodUid, - nextAdGroupIndex, - adIndexInAdGroup, - /* contentPositionUs= */ mediaPeriodInfo.durationUs, - currentPeriodId.windowSequenceNumber); + return getMediaPeriodInfoForAd( + timeline, + currentPeriodId.periodUid, + nextAdGroupIndex, + adIndexInAdGroup, + /* contentPositionUs= */ mediaPeriodInfo.durationUs, + currentPeriodId.windowSequenceNumber); } } @@ -737,9 +733,6 @@ import com.google.common.collect.ImmutableList; Timeline timeline, MediaPeriodId id, long requestedContentPositionUs, long startPositionUs) { timeline.getPeriodByUid(id.periodUid, period); if (id.isAd()) { - if (!period.isAdAvailable(id.adGroupIndex, id.adIndexInAdGroup)) { - return null; - } return getMediaPeriodInfoForAd( timeline, id.periodUid, diff --git a/library/core/src/main/java/com/google/android/exoplayer2/Timeline.java b/library/core/src/main/java/com/google/android/exoplayer2/Timeline.java index e992eb588d..19be5bf9d0 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/Timeline.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/Timeline.java @@ -166,7 +166,7 @@ public abstract class Timeline { /** * The window's start time in milliseconds since the Unix epoch, or {@link C#TIME_UNSET} if - * unknown or not applicable. For informational purposes only. + * unknown or not applicable. */ public long windowStartTimeMs; @@ -609,19 +609,6 @@ public abstract class Timeline { return adPlaybackState.adGroups[adGroupIndex].count; } - /** - * Returns whether the URL for the specified ad is known. - * - * @param adGroupIndex The ad group index. - * @param adIndexInAdGroup The ad index in the ad group. - * @return Whether the URL for the specified ad is known. - */ - public boolean isAdAvailable(int adGroupIndex, int adIndexInAdGroup) { - AdPlaybackState.AdGroup adGroup = adPlaybackState.adGroups[adGroupIndex]; - return adGroup.count != C.LENGTH_UNSET - && adGroup.states[adIndexInAdGroup] != AdPlaybackState.AD_STATE_UNAVAILABLE; - } - /** * Returns the duration of the ad at index {@code adIndexInAdGroup} in the ad group at * {@code adGroupIndex}, in microseconds, or {@link C#TIME_UNSET} if not yet known. diff --git a/library/core/src/main/java/com/google/android/exoplayer2/audio/DefaultAudioSink.java b/library/core/src/main/java/com/google/android/exoplayer2/audio/DefaultAudioSink.java index 26ec4b94c2..691580143a 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/audio/DefaultAudioSink.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/audio/DefaultAudioSink.java @@ -869,7 +869,9 @@ public final class DefaultAudioSink implements AudioSink { writeBuffer(input, avSyncPresentationTimeUs); } else { AudioProcessor audioProcessor = activeAudioProcessors[index]; - audioProcessor.queueInput(input); + if (index > drainingAudioProcessorIndex) { + audioProcessor.queueInput(input); + } ByteBuffer output = audioProcessor.getOutput(); outputBuffers[index] = output; if (output.hasRemaining()) { diff --git a/library/core/src/main/java/com/google/android/exoplayer2/audio/SonicAudioProcessor.java b/library/core/src/main/java/com/google/android/exoplayer2/audio/SonicAudioProcessor.java index ef5bbbc078..f859761954 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/audio/SonicAudioProcessor.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/audio/SonicAudioProcessor.java @@ -165,32 +165,20 @@ public final class SonicAudioProcessor implements AudioProcessor { @Override public void queueInput(ByteBuffer inputBuffer) { + if (!inputBuffer.hasRemaining()) { + return; + } Sonic sonic = checkNotNull(this.sonic); - if (inputBuffer.hasRemaining()) { - ShortBuffer shortBuffer = inputBuffer.asShortBuffer(); - int inputSize = inputBuffer.remaining(); - inputBytes += inputSize; - sonic.queueInput(shortBuffer); - inputBuffer.position(inputBuffer.position() + inputSize); - } - int outputSize = sonic.getOutputSize(); - if (outputSize > 0) { - if (buffer.capacity() < outputSize) { - buffer = ByteBuffer.allocateDirect(outputSize).order(ByteOrder.nativeOrder()); - shortBuffer = buffer.asShortBuffer(); - } else { - buffer.clear(); - shortBuffer.clear(); - } - sonic.getOutput(shortBuffer); - outputBytes += outputSize; - buffer.limit(outputSize); - outputBuffer = buffer; - } + ShortBuffer shortBuffer = inputBuffer.asShortBuffer(); + int inputSize = inputBuffer.remaining(); + inputBytes += inputSize; + sonic.queueInput(shortBuffer); + inputBuffer.position(inputBuffer.position() + inputSize); } @Override public void queueEndOfStream() { + // TODO(internal b/174554082): assert sonic is non-null here and in getOutput. if (sonic != null) { sonic.queueEndOfStream(); } @@ -199,6 +187,23 @@ public final class SonicAudioProcessor implements AudioProcessor { @Override public ByteBuffer getOutput() { + @Nullable Sonic sonic = this.sonic; + if (sonic != null) { + int outputSize = sonic.getOutputSize(); + if (outputSize > 0) { + if (buffer.capacity() < outputSize) { + buffer = ByteBuffer.allocateDirect(outputSize).order(ByteOrder.nativeOrder()); + shortBuffer = buffer.asShortBuffer(); + } else { + buffer.clear(); + shortBuffer.clear(); + } + sonic.getOutput(shortBuffer); + outputBytes += outputSize; + buffer.limit(outputSize); + outputBuffer = buffer; + } + } ByteBuffer outputBuffer = this.outputBuffer; this.outputBuffer = EMPTY_BUFFER; return outputBuffer; diff --git a/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/BatchBuffer.java b/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/BatchBuffer.java index 3c40fe02d4..f770e92a21 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/BatchBuffer.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/BatchBuffer.java @@ -153,26 +153,26 @@ import java.nio.ByteBuffer; } private void putAccessUnit(DecoderInputBuffer accessUnit) { - @Nullable ByteBuffer accessUnitData = accessUnit.data; - if (accessUnitData != null) { - accessUnit.flip(); - ensureSpaceForWrite(accessUnitData.remaining()); - this.data.put(accessUnitData); - } - if (accessUnit.isEndOfStream()) { setFlags(C.BUFFER_FLAG_END_OF_STREAM); - } - if (accessUnit.isDecodeOnly()) { - setFlags(C.BUFFER_FLAG_DECODE_ONLY); - } - if (accessUnit.isKeyFrame()) { - setFlags(C.BUFFER_FLAG_KEY_FRAME); - } - accessUnitCount++; - timeUs = accessUnit.timeUs; - if (accessUnitCount == 1) { // First read of the buffer - firstAccessUnitTimeUs = timeUs; + } else { + timeUs = accessUnit.timeUs; + if (accessUnit.isDecodeOnly()) { + setFlags(C.BUFFER_FLAG_DECODE_ONLY); + } + if (accessUnit.isKeyFrame()) { + setFlags(C.BUFFER_FLAG_KEY_FRAME); + } + @Nullable ByteBuffer accessUnitData = accessUnit.data; + if (accessUnitData != null) { + accessUnit.flip(); + ensureSpaceForWrite(accessUnitData.remaining()); + this.data.put(accessUnitData); + } + accessUnitCount++; + if (accessUnitCount == 1) { + firstAccessUnitTimeUs = timeUs; + } } accessUnit.clear(); } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecRenderer.java b/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecRenderer.java index a2ba72dbc0..75ee7f5c81 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecRenderer.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/mediacodec/MediaCodecRenderer.java @@ -450,6 +450,11 @@ public abstract class MediaCodecRenderer extends BaseRenderer { outputStreamStartPositionUs = C.TIME_UNSET; outputStreamOffsetUs = C.TIME_UNSET; bypassBatchBuffer = new BatchBuffer(); + bypassBatchBuffer.ensureSpaceForWrite(/* length= */ 0); + // MediaCodec outputs audio buffers in native endian: + // https://developer.android.com/reference/android/media/MediaCodec#raw-audio-buffers + // and code called from MediaCodecAudioRenderer.processOutputBuffer expects this endianness. + bypassBatchBuffer.data.order(ByteOrder.nativeOrder()); resetCodecStateForRelease(); } @@ -1455,6 +1460,11 @@ public abstract class MediaCodecRenderer extends BaseRenderer { protected void onInputFormatChanged(FormatHolder formatHolder) throws ExoPlaybackException { waitingForFirstSampleInFormat = true; Format newFormat = Assertions.checkNotNull(formatHolder.format); + if (newFormat.sampleMimeType == null) { + // If the new format is invalid, it is either a media bug or it is not intended to be played. + // See also https://github.com/google/ExoPlayer/issues/8283. + throw createRendererException(new IllegalArgumentException(), newFormat); + } setSourceDrmSession(formatHolder.drmSession); inputFormat = newFormat; @@ -2129,7 +2139,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer { * iteration of the rendering loop. * @param elapsedRealtimeUs {@link SystemClock#elapsedRealtime()} in microseconds, measured at the * start of the current iteration of the rendering loop. - * @return If more buffers are ready to be rendered. + * @return Whether immediately calling this method again will make more progress. * @throws ExoPlaybackException If an error occurred while processing a buffer or handling a * format change. */ @@ -2137,7 +2147,7 @@ public abstract class MediaCodecRenderer extends BaseRenderer { throws ExoPlaybackException { BatchBuffer batchBuffer = bypassBatchBuffer; - // Let's process the pending buffer if any. + // Process any data in the batch buffer. checkState(!outputStreamEnded); if (!batchBuffer.isEmpty()) { // Optimisation: Do not process buffer if empty. if (processOutputBuffer( @@ -2152,12 +2162,13 @@ public abstract class MediaCodecRenderer extends BaseRenderer { batchBuffer.isDecodeOnly(), batchBuffer.isEndOfStream(), outputFormat)) { - // Buffer completely processed onProcessedOutputBuffer(batchBuffer.getLastAccessUnitTimeUs()); } else { - return false; // Could not process buffer, let's try later. + // Could not process the whole buffer. Try again later. + return false; } } + // Process the end of stream, if it has been reached. if (batchBuffer.isEndOfStream()) { outputStreamEnded = true; return false; @@ -2192,19 +2203,17 @@ public abstract class MediaCodecRenderer extends BaseRenderer { onInputFormatChanged(formatHolder); } + boolean haveDataToProcess = false; if (batchBuffer.isEndOfStream()) { inputStreamEnded = true; + haveDataToProcess = true; + } + if (!batchBuffer.isEmpty()) { + batchBuffer.flip(); + haveDataToProcess = true; } - if (batchBuffer.isEmpty()) { - return false; // The buffer could not be filled, there is nothing more to do. - } - batchBuffer.flip(); // Buffer at least partially full, it can now be processed. - // MediaCodec outputs buffers in native endian: - // https://developer.android.com/reference/android/media/MediaCodec#raw-audio-buffers - // and code called from processOutputBuffer expects this endianness. - batchBuffer.data.order(ByteOrder.nativeOrder()); - return true; + return haveDataToProcess; } /** diff --git a/library/core/src/main/java/com/google/android/exoplayer2/offline/DefaultDownloadIndex.java b/library/core/src/main/java/com/google/android/exoplayer2/offline/DefaultDownloadIndex.java index d9a060fe2d..3a18d4da10 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/offline/DefaultDownloadIndex.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/offline/DefaultDownloadIndex.java @@ -21,6 +21,7 @@ import android.database.SQLException; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteException; import android.net.Uri; +import androidx.annotation.GuardedBy; import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import com.google.android.exoplayer2.database.DatabaseIOException; @@ -136,7 +137,9 @@ public final class DefaultDownloadIndex implements WritableDownloadIndex { private final String name; private final String tableName; private final DatabaseProvider databaseProvider; + private final Object initializationLock; + @GuardedBy("initializationLock") private boolean initialized; /** @@ -168,6 +171,7 @@ public final class DefaultDownloadIndex implements WritableDownloadIndex { this.name = name; this.databaseProvider = databaseProvider; tableName = TABLE_PREFIX + name; + initializationLock = new Object(); } @Override @@ -273,33 +277,35 @@ public final class DefaultDownloadIndex implements WritableDownloadIndex { } private void ensureInitialized() throws DatabaseIOException { - if (initialized) { - return; - } - try { - SQLiteDatabase readableDatabase = databaseProvider.getReadableDatabase(); - int version = VersionTable.getVersion(readableDatabase, VersionTable.FEATURE_OFFLINE, name); - if (version != TABLE_VERSION) { - SQLiteDatabase writableDatabase = databaseProvider.getWritableDatabase(); - writableDatabase.beginTransactionNonExclusive(); - try { - VersionTable.setVersion( - writableDatabase, VersionTable.FEATURE_OFFLINE, name, TABLE_VERSION); - List upgradedDownloads = - version == 2 ? loadDownloadsFromVersion2(writableDatabase) : new ArrayList<>(); - writableDatabase.execSQL("DROP TABLE IF EXISTS " + tableName); - writableDatabase.execSQL("CREATE TABLE " + tableName + " " + TABLE_SCHEMA); - for (Download download : upgradedDownloads) { - putDownloadInternal(download, writableDatabase); - } - writableDatabase.setTransactionSuccessful(); - } finally { - writableDatabase.endTransaction(); - } + synchronized (initializationLock) { + if (initialized) { + return; + } + try { + SQLiteDatabase readableDatabase = databaseProvider.getReadableDatabase(); + int version = VersionTable.getVersion(readableDatabase, VersionTable.FEATURE_OFFLINE, name); + if (version != TABLE_VERSION) { + SQLiteDatabase writableDatabase = databaseProvider.getWritableDatabase(); + writableDatabase.beginTransactionNonExclusive(); + try { + VersionTable.setVersion( + writableDatabase, VersionTable.FEATURE_OFFLINE, name, TABLE_VERSION); + List upgradedDownloads = + version == 2 ? loadDownloadsFromVersion2(writableDatabase) : new ArrayList<>(); + writableDatabase.execSQL("DROP TABLE IF EXISTS " + tableName); + writableDatabase.execSQL("CREATE TABLE " + tableName + " " + TABLE_SCHEMA); + for (Download download : upgradedDownloads) { + putDownloadInternal(download, writableDatabase); + } + writableDatabase.setTransactionSuccessful(); + } finally { + writableDatabase.endTransaction(); + } + } + initialized = true; + } catch (SQLException e) { + throw new DatabaseIOException(e); } - initialized = true; - } catch (SQLException e) { - throw new DatabaseIOException(e); } } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/offline/DownloadManager.java b/library/core/src/main/java/com/google/android/exoplayer2/offline/DownloadManager.java index b6228025cf..a989e2575f 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/offline/DownloadManager.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/offline/DownloadManager.java @@ -856,7 +856,7 @@ public final class DownloadManager { private void setStopReason(Download download, int stopReason) { if (stopReason == STOP_REASON_NONE) { if (download.state == STATE_STOPPED) { - putDownloadWithState(download, STATE_QUEUED); + putDownloadWithState(download, STATE_QUEUED, STOP_REASON_NONE); } } else if (stopReason != download.stopReason) { @Download.State int state = download.state; @@ -910,7 +910,7 @@ public final class DownloadManager { Log.e(TAG, "Failed to remove nonexistent download: " + id); return; } - putDownloadWithState(download, STATE_REMOVING); + putDownloadWithState(download, STATE_REMOVING, STOP_REASON_NONE); syncTasks(); } @@ -924,10 +924,11 @@ public final class DownloadManager { Log.e(TAG, "Failed to load downloads."); } for (int i = 0; i < downloads.size(); i++) { - downloads.set(i, copyDownloadWithState(downloads.get(i), STATE_REMOVING)); + downloads.set(i, copyDownloadWithState(downloads.get(i), STATE_REMOVING, STOP_REASON_NONE)); } for (int i = 0; i < terminalDownloads.size(); i++) { - downloads.add(copyDownloadWithState(terminalDownloads.get(i), STATE_REMOVING)); + downloads.add( + copyDownloadWithState(terminalDownloads.get(i), STATE_REMOVING, STOP_REASON_NONE)); } Collections.sort(downloads, InternalHandler::compareStartTimes); try { @@ -1019,7 +1020,7 @@ public final class DownloadManager { } // We can start a download task. - download = putDownloadWithState(download, STATE_DOWNLOADING); + download = putDownloadWithState(download, STATE_DOWNLOADING, STOP_REASON_NONE); Downloader downloader = downloaderFactory.createDownloader(download.request); activeTask = new Task( @@ -1041,7 +1042,7 @@ public final class DownloadManager { Task activeTask, Download download, int accumulatingDownloadTaskCount) { Assertions.checkState(!activeTask.isRemove); if (!canDownloadsRun() || accumulatingDownloadTaskCount >= maxParallelDownloads) { - putDownloadWithState(download, STATE_QUEUED); + putDownloadWithState(download, STATE_QUEUED, STOP_REASON_NONE); activeTask.cancel(/* released= */ false); } } @@ -1161,8 +1162,9 @@ public final class DownloadManager { private void onRemoveTaskStopped(Download download) { if (download.state == STATE_RESTARTING) { - putDownloadWithState( - download, download.stopReason == STOP_REASON_NONE ? STATE_QUEUED : STATE_STOPPED); + @Download.State + int state = download.stopReason == STOP_REASON_NONE ? STATE_QUEUED : STATE_STOPPED; + putDownloadWithState(download, state, download.stopReason); syncTasks(); } else { int removeIndex = getDownloadIndex(download.request.id); @@ -1204,12 +1206,11 @@ public final class DownloadManager { return !downloadsPaused && notMetRequirements == 0; } - private Download putDownloadWithState(Download download, @Download.State int state) { - // Downloads in terminal states shouldn't be in the downloads list. This method cannot be used - // to set STATE_STOPPED either, because it doesn't have a stopReason argument. - Assertions.checkState( - state != STATE_COMPLETED && state != STATE_FAILED && state != STATE_STOPPED); - return putDownload(copyDownloadWithState(download, state)); + private Download putDownloadWithState( + Download download, @Download.State int state, int stopReason) { + // Downloads in terminal states shouldn't be in the downloads list. + Assertions.checkState(state != STATE_COMPLETED && state != STATE_FAILED); + return putDownload(copyDownloadWithState(download, state, stopReason)); } private Download putDownload(Download download) { @@ -1267,14 +1268,15 @@ public final class DownloadManager { return C.INDEX_UNSET; } - private static Download copyDownloadWithState(Download download, @Download.State int state) { + private static Download copyDownloadWithState( + Download download, @Download.State int state, int stopReason) { return new Download( download.request, state, download.startTimeMs, /* updateTimeMs= */ System.currentTimeMillis(), download.contentLength, - /* stopReason= */ 0, + stopReason, FAILURE_REASON_NONE, download.progress); } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/DefaultMediaSourceFactory.java b/library/core/src/main/java/com/google/android/exoplayer2/source/DefaultMediaSourceFactory.java index 8f85a0ac17..b3955d50c3 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/source/DefaultMediaSourceFactory.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/source/DefaultMediaSourceFactory.java @@ -251,7 +251,8 @@ public final class DefaultMediaSourceFactory implements MediaSourceFactory { MediaSource[] mediaSources = new MediaSource[subtitles.size() + 1]; mediaSources[0] = mediaSource; SingleSampleMediaSource.Factory singleSampleSourceFactory = - new SingleSampleMediaSource.Factory(dataSourceFactory); + new SingleSampleMediaSource.Factory(dataSourceFactory) + .setLoadErrorHandlingPolicy(loadErrorHandlingPolicy); for (int i = 0; i < subtitles.size(); i++) { mediaSources[i + 1] = singleSampleSourceFactory.createMediaSource( diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/MaskingMediaPeriod.java b/library/core/src/main/java/com/google/android/exoplayer2/source/MaskingMediaPeriod.java index 9514241035..a69835532f 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/source/MaskingMediaPeriod.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/source/MaskingMediaPeriod.java @@ -15,6 +15,8 @@ */ package com.google.android.exoplayer2.source; +import static com.google.android.exoplayer2.util.Assertions.checkNotNull; +import static com.google.android.exoplayer2.util.Assertions.checkState; import static com.google.android.exoplayer2.util.Util.castNonNull; import androidx.annotation.Nullable; @@ -25,12 +27,13 @@ import com.google.android.exoplayer2.trackselection.TrackSelection; import com.google.android.exoplayer2.upstream.Allocator; import java.io.IOException; import org.checkerframework.checker.nullness.compatqual.NullableType; +import org.checkerframework.checker.nullness.qual.MonotonicNonNull; /** - * Media period that wraps a media source and defers calling its {@link - * MediaSource#createPeriod(MediaPeriodId, Allocator, long)} method until {@link - * #createPeriod(MediaPeriodId)} has been called. This is useful if you need to return a media - * period immediately but the media source that should create it is not yet prepared. + * Media period that defers calling {@link MediaSource#createPeriod(MediaPeriodId, Allocator, long)} + * on a given source until {@link #createPeriod(MediaPeriodId)} has been called. This is useful if + * you need to return a media period immediately but the media source that should create it is not + * yet available or prepared. */ public final class MaskingMediaPeriod implements MediaPeriod, MediaPeriod.Callback { @@ -46,33 +49,32 @@ public final class MaskingMediaPeriod implements MediaPeriod, MediaPeriod.Callba void onPrepareError(MediaPeriodId mediaPeriodId, IOException exception); } - /** The {@link MediaSource} which will create the actual media period. */ - public final MediaSource mediaSource; /** The {@link MediaPeriodId} used to create the masking media period. */ public final MediaPeriodId id; + private final long preparePositionUs; private final Allocator allocator; - @Nullable private MediaPeriod mediaPeriod; + /** The {@link MediaSource} that will create the underlying media period. */ + private @MonotonicNonNull MediaSource mediaSource; + + private @MonotonicNonNull MediaPeriod mediaPeriod; @Nullable private Callback callback; - private long preparePositionUs; @Nullable private PrepareListener listener; private boolean notifiedPrepareError; private long preparePositionOverrideUs; /** - * Creates a new masking media period. + * Creates a new masking media period. The media source must be set via {@link + * #setMediaSource(MediaSource)} before preparation can start. * - * @param mediaSource The media source to wrap. * @param id The identifier used to create the masking media period. * @param allocator The allocator used to create the media period. * @param preparePositionUs The expected start position, in microseconds. */ - public MaskingMediaPeriod( - MediaSource mediaSource, MediaPeriodId id, Allocator allocator, long preparePositionUs) { + public MaskingMediaPeriod(MediaPeriodId id, Allocator allocator, long preparePositionUs) { this.id = id; this.allocator = allocator; - this.mediaSource = mediaSource; this.preparePositionUs = preparePositionUs; preparePositionOverrideUs = C.TIME_UNSET; } @@ -108,6 +110,12 @@ public final class MaskingMediaPeriod implements MediaPeriod, MediaPeriod.Callba return preparePositionOverrideUs; } + /** Sets the {@link MediaSource} that will create the underlying media period. */ + public void setMediaSource(MediaSource mediaSource) { + checkState(this.mediaSource == null); + this.mediaSource = mediaSource; + } + /** * Calls {@link MediaSource#createPeriod(MediaPeriodId, Allocator, long)} on the wrapped source * then prepares it if {@link #prepare(Callback, long)} has been called. Call {@link @@ -117,18 +125,16 @@ public final class MaskingMediaPeriod implements MediaPeriod, MediaPeriod.Callba */ public void createPeriod(MediaPeriodId id) { long preparePositionUs = getPreparePositionWithOverride(this.preparePositionUs); - mediaPeriod = mediaSource.createPeriod(id, allocator, preparePositionUs); + mediaPeriod = checkNotNull(mediaSource).createPeriod(id, allocator, preparePositionUs); if (callback != null) { - mediaPeriod.prepare(this, preparePositionUs); + mediaPeriod.prepare(/* callback= */ this, preparePositionUs); } } - /** - * Releases the period. - */ + /** Releases the period. */ public void releasePeriod() { if (mediaPeriod != null) { - mediaSource.releasePeriod(mediaPeriod); + checkNotNull(mediaSource).releasePeriod(mediaPeriod); } } @@ -136,7 +142,8 @@ public final class MaskingMediaPeriod implements MediaPeriod, MediaPeriod.Callba public void prepare(Callback callback, long preparePositionUs) { this.callback = callback; if (mediaPeriod != null) { - mediaPeriod.prepare(this, getPreparePositionWithOverride(this.preparePositionUs)); + mediaPeriod.prepare( + /* callback= */ this, getPreparePositionWithOverride(this.preparePositionUs)); } } @@ -145,10 +152,10 @@ public final class MaskingMediaPeriod implements MediaPeriod, MediaPeriod.Callba try { if (mediaPeriod != null) { mediaPeriod.maybeThrowPrepareError(); - } else { + } else if (mediaSource != null) { mediaSource.maybeThrowSourceInfoRefreshError(); } - } catch (final IOException e) { + } catch (IOException e) { if (listener == null) { throw e; } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/MaskingMediaSource.java b/library/core/src/main/java/com/google/android/exoplayer2/source/MaskingMediaSource.java index 19f5df2aa5..5d2e1c6fb7 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/source/MaskingMediaSource.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/source/MaskingMediaSource.java @@ -111,8 +111,8 @@ public final class MaskingMediaSource extends CompositeMediaSource { @Override public MaskingMediaPeriod createPeriod( MediaPeriodId id, Allocator allocator, long startPositionUs) { - MaskingMediaPeriod mediaPeriod = - new MaskingMediaPeriod(mediaSource, id, allocator, startPositionUs); + MaskingMediaPeriod mediaPeriod = new MaskingMediaPeriod(id, allocator, startPositionUs); + mediaPeriod.setMediaSource(mediaSource); if (isPrepared) { MediaPeriodId idInSource = id.copyWithPeriodUid(getInternalPeriodUid(id.periodUid)); mediaPeriod.createPeriod(idInSource); diff --git a/library/core/src/main/java/com/google/android/exoplayer2/source/ads/AdsMediaSource.java b/library/core/src/main/java/com/google/android/exoplayer2/source/ads/AdsMediaSource.java index 7320f6f6c5..10e9d8e3be 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/source/ads/AdsMediaSource.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/source/ads/AdsMediaSource.java @@ -15,6 +15,8 @@ */ package com.google.android.exoplayer2.source.ads; +import static com.google.android.exoplayer2.util.Assertions.checkNotNull; + import android.net.Uri; import android.os.Handler; import android.os.Looper; @@ -116,7 +118,7 @@ public final class AdsMediaSource extends CompositeMediaSource { */ public RuntimeException getRuntimeExceptionForUnexpected() { Assertions.checkState(type == TYPE_UNEXPECTED); - return (RuntimeException) Assertions.checkNotNull(getCause()); + return (RuntimeException) checkNotNull(getCause()); } } @@ -257,12 +259,10 @@ public final class AdsMediaSource extends CompositeMediaSource { @Override public MediaPeriod createPeriod(MediaPeriodId id, Allocator allocator, long startPositionUs) { - AdPlaybackState adPlaybackState = Assertions.checkNotNull(this.adPlaybackState); + AdPlaybackState adPlaybackState = checkNotNull(this.adPlaybackState); if (adPlaybackState.adGroupCount > 0 && id.isAd()) { int adGroupIndex = id.adGroupIndex; int adIndexInAdGroup = id.adIndexInAdGroup; - Uri adUri = - Assertions.checkNotNull(adPlaybackState.adGroups[adGroupIndex].uris[adIndexInAdGroup]); if (adMediaSourceHolders[adGroupIndex].length <= adIndexInAdGroup) { int adCount = adIndexInAdGroup + 1; adMediaSourceHolders[adGroupIndex] = @@ -272,16 +272,14 @@ public final class AdsMediaSource extends CompositeMediaSource { AdMediaSourceHolder adMediaSourceHolder = adMediaSourceHolders[adGroupIndex][adIndexInAdGroup]; if (adMediaSourceHolder == null) { - MediaSource adMediaSource = - adMediaSourceFactory.createMediaSource(MediaItem.fromUri(adUri)); - adMediaSourceHolder = new AdMediaSourceHolder(adMediaSource); + adMediaSourceHolder = new AdMediaSourceHolder(id); adMediaSourceHolders[adGroupIndex][adIndexInAdGroup] = adMediaSourceHolder; - prepareChildSource(id, adMediaSource); + maybeUpdateAdMediaSources(); } - return adMediaSourceHolder.createMediaPeriod(adUri, id, allocator, startPositionUs); + return adMediaSourceHolder.createMediaPeriod(id, allocator, startPositionUs); } else { - MaskingMediaPeriod mediaPeriod = - new MaskingMediaPeriod(contentMediaSource, id, allocator, startPositionUs); + MaskingMediaPeriod mediaPeriod = new MaskingMediaPeriod(id, allocator, startPositionUs); + mediaPeriod.setMediaSource(contentMediaSource); mediaPeriod.createPeriod(id); return mediaPeriod; } @@ -293,10 +291,10 @@ public final class AdsMediaSource extends CompositeMediaSource { MediaPeriodId id = maskingMediaPeriod.id; if (id.isAd()) { AdMediaSourceHolder adMediaSourceHolder = - Assertions.checkNotNull(adMediaSourceHolders[id.adGroupIndex][id.adIndexInAdGroup]); + checkNotNull(adMediaSourceHolders[id.adGroupIndex][id.adIndexInAdGroup]); adMediaSourceHolder.releaseMediaPeriod(maskingMediaPeriod); if (adMediaSourceHolder.isInactive()) { - releaseChildSource(id); + adMediaSourceHolder.release(); adMediaSourceHolders[id.adGroupIndex][id.adIndexInAdGroup] = null; } } else { @@ -307,7 +305,7 @@ public final class AdsMediaSource extends CompositeMediaSource { @Override protected void releaseSourceInternal() { super.releaseSourceInternal(); - Assertions.checkNotNull(componentListener).release(); + checkNotNull(componentListener).release(); componentListener = null; contentTimeline = null; adPlaybackState = null; @@ -321,7 +319,7 @@ public final class AdsMediaSource extends CompositeMediaSource { if (mediaPeriodId.isAd()) { int adGroupIndex = mediaPeriodId.adGroupIndex; int adIndexInAdGroup = mediaPeriodId.adIndexInAdGroup; - Assertions.checkNotNull(adMediaSourceHolders[adGroupIndex][adIndexInAdGroup]) + checkNotNull(adMediaSourceHolders[adGroupIndex][adIndexInAdGroup]) .handleSourceInfoRefresh(timeline); } else { Assertions.checkArgument(timeline.getPeriodCount() == 1); @@ -346,9 +344,41 @@ public final class AdsMediaSource extends CompositeMediaSource { Arrays.fill(adMediaSourceHolders, new AdMediaSourceHolder[0]); } this.adPlaybackState = adPlaybackState; + maybeUpdateAdMediaSources(); maybeUpdateSourceInfo(); } + /** + * Initializes any {@link AdMediaSourceHolder AdMediaSourceHolders} where the ad media URI is + * newly known. + */ + private void maybeUpdateAdMediaSources() { + @Nullable AdPlaybackState adPlaybackState = this.adPlaybackState; + if (adPlaybackState == null) { + return; + } + for (int adGroupIndex = 0; adGroupIndex < adMediaSourceHolders.length; adGroupIndex++) { + for (int adIndexInAdGroup = 0; + adIndexInAdGroup < this.adMediaSourceHolders[adGroupIndex].length; + adIndexInAdGroup++) { + @Nullable + AdMediaSourceHolder adMediaSourceHolder = + this.adMediaSourceHolders[adGroupIndex][adIndexInAdGroup]; + if (adMediaSourceHolder != null + && !adMediaSourceHolder.hasMediaSource() + && adPlaybackState.adGroups[adGroupIndex] != null + && adIndexInAdGroup < adPlaybackState.adGroups[adGroupIndex].uris.length) { + @Nullable Uri adUri = adPlaybackState.adGroups[adGroupIndex].uris[adIndexInAdGroup]; + if (adUri != null) { + MediaSource adMediaSource = + adMediaSourceFactory.createMediaSource(MediaItem.fromUri(adUri)); + adMediaSourceHolder.initializeWithMediaSource(adMediaSource, adUri); + } + } + } + } + } + private void maybeUpdateSourceInfo() { @Nullable Timeline contentTimeline = this.contentTimeline; if (adPlaybackState != null && contentTimeline != null) { @@ -461,22 +491,38 @@ public final class AdsMediaSource extends CompositeMediaSource { private final class AdMediaSourceHolder { - private final MediaSource adMediaSource; + private final MediaPeriodId id; private final List activeMediaPeriods; + private @MonotonicNonNull Uri adUri; + private @MonotonicNonNull MediaSource adMediaSource; private @MonotonicNonNull Timeline timeline; - public AdMediaSourceHolder(MediaSource adMediaSource) { - this.adMediaSource = adMediaSource; + public AdMediaSourceHolder(MediaPeriodId id) { + this.id = id; activeMediaPeriods = new ArrayList<>(); } + public void initializeWithMediaSource(MediaSource adMediaSource, Uri adUri) { + this.adMediaSource = adMediaSource; + this.adUri = adUri; + for (int i = 0; i < activeMediaPeriods.size(); i++) { + MaskingMediaPeriod maskingMediaPeriod = activeMediaPeriods.get(i); + maskingMediaPeriod.setMediaSource(adMediaSource); + maskingMediaPeriod.setPrepareListener(new AdPrepareListener(adUri)); + } + prepareChildSource(id, adMediaSource); + } + public MediaPeriod createMediaPeriod( - Uri adUri, MediaPeriodId id, Allocator allocator, long startPositionUs) { + MediaPeriodId id, Allocator allocator, long startPositionUs) { MaskingMediaPeriod maskingMediaPeriod = - new MaskingMediaPeriod(adMediaSource, id, allocator, startPositionUs); - maskingMediaPeriod.setPrepareListener(new AdPrepareListener(adUri)); + new MaskingMediaPeriod(id, allocator, startPositionUs); activeMediaPeriods.add(maskingMediaPeriod); + if (adMediaSource != null) { + maskingMediaPeriod.setMediaSource(adMediaSource); + maskingMediaPeriod.setPrepareListener(new AdPrepareListener(checkNotNull(adUri))); + } if (timeline != null) { Object periodUid = timeline.getUidOfPeriod(/* periodIndex= */ 0); MediaPeriodId adSourceMediaPeriodId = new MediaPeriodId(periodUid, id.windowSequenceNumber); @@ -510,6 +556,16 @@ public final class AdsMediaSource extends CompositeMediaSource { maskingMediaPeriod.releasePeriod(); } + public void release() { + if (hasMediaSource()) { + releaseChildSource(id); + } + } + + public boolean hasMediaSource() { + return adMediaSource != null; + } + public boolean isInactive() { return activeMediaPeriods.isEmpty(); } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/text/cea/Cea708Decoder.java b/library/core/src/main/java/com/google/android/exoplayer2/text/cea/Cea708Decoder.java index 8bd46fabdc..56dd4ebef2 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/text/cea/Cea708Decoder.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/text/cea/Cea708Decoder.java @@ -1226,18 +1226,18 @@ public final class Cea708Decoder extends CeaDecoder { // | | // 6-----7-----8 @AnchorType int verticalAnchorType; - if (anchorId % 3 == 0) { + if (anchorId / 3 == 0) { verticalAnchorType = Cue.ANCHOR_TYPE_START; - } else if (anchorId % 3 == 1) { + } else if (anchorId / 3 == 1) { verticalAnchorType = Cue.ANCHOR_TYPE_MIDDLE; } else { verticalAnchorType = Cue.ANCHOR_TYPE_END; } // TODO: Add support for right-to-left languages (i.e. where start is on the right). @AnchorType int horizontalAnchorType; - if (anchorId / 3 == 0) { + if (anchorId % 3 == 0) { horizontalAnchorType = Cue.ANCHOR_TYPE_START; - } else if (anchorId / 3 == 1) { + } else if (anchorId % 3 == 1) { horizontalAnchorType = Cue.ANCHOR_TYPE_MIDDLE; } else { horizontalAnchorType = Cue.ANCHOR_TYPE_END; diff --git a/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/SimpleCache.java b/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/SimpleCache.java index 29c09ff486..cc1e5a8e5e 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/SimpleCache.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/upstream/cache/SimpleCache.java @@ -409,18 +409,20 @@ public final class SimpleCache implements Cache { Assertions.checkNotNull(cachedContent); Assertions.checkState(cachedContent.isFullyLocked(position, length)); if (!cacheDir.exists()) { - // For some reason the cache directory doesn't exist. Make a best effort to create it. - cacheDir.mkdirs(); + // The cache directory has been deleted from underneath us. Recreate it, and remove in-memory + // spans corresponding to cache files that no longer exist. + createCacheDirectories(cacheDir); removeStaleSpans(); } evictor.onStartFile(this, key, position, length); // Randomly distribute files into subdirectories with a uniform distribution. - File fileDir = new File(cacheDir, Integer.toString(random.nextInt(SUBDIRECTORY_COUNT))); - if (!fileDir.exists()) { - fileDir.mkdir(); + File cacheSubDir = new File(cacheDir, Integer.toString(random.nextInt(SUBDIRECTORY_COUNT))); + if (!cacheSubDir.exists()) { + createCacheDirectories(cacheSubDir); } long lastTouchTimestamp = System.currentTimeMillis(); - return SimpleCacheSpan.getCacheFile(fileDir, cachedContent.id, position, lastTouchTimestamp); + return SimpleCacheSpan.getCacheFile( + cacheSubDir, cachedContent.id, position, lastTouchTimestamp); } @Override @@ -548,10 +550,10 @@ public final class SimpleCache implements Cache { /** Ensures that the cache's in-memory representation has been initialized. */ private void initialize() { if (!cacheDir.exists()) { - if (!cacheDir.mkdirs()) { - String message = "Failed to create cache directory: " + cacheDir; - Log.e(TAG, message); - initializationException = new CacheException(message); + try { + createCacheDirectories(cacheDir); + } catch (CacheException e) { + initializationException = e; return; } } @@ -834,6 +836,14 @@ public final class SimpleCache implements Cache { return Long.parseLong(fileName.substring(0, fileName.indexOf('.')), /* radix= */ 16); } + private static void createCacheDirectories(File cacheDir) throws CacheException { + if (!cacheDir.mkdirs()) { + String message = "Failed to create cache directory: " + cacheDir; + Log.e(TAG, message); + throw new CacheException(message); + } + } + private static synchronized boolean lockFolder(File cacheDir) { return lockedCacheDirs.add(cacheDir.getAbsoluteFile()); } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/util/EventLogger.java b/library/core/src/main/java/com/google/android/exoplayer2/util/EventLogger.java index a441e81bc4..53afd7a7a3 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/util/EventLogger.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/util/EventLogger.java @@ -179,9 +179,9 @@ public class EventLogger implements AnalyticsListener { " " + "window [" + getTimeString(window.getDurationMs()) - + ", " + + ", seekable=" + window.isSeekable - + ", " + + ", dynamic=" + window.isDynamic + "]"); } diff --git a/library/core/src/main/java/com/google/android/exoplayer2/video/MediaCodecVideoRenderer.java b/library/core/src/main/java/com/google/android/exoplayer2/video/MediaCodecVideoRenderer.java index 0f6ea28ed9..8da7866d0c 100644 --- a/library/core/src/main/java/com/google/android/exoplayer2/video/MediaCodecVideoRenderer.java +++ b/library/core/src/main/java/com/google/android/exoplayer2/video/MediaCodecVideoRenderer.java @@ -1577,16 +1577,18 @@ public class MediaCodecVideoRenderer extends MediaCodecRenderer { private static boolean evaluateDeviceNeedsSetOutputSurfaceWorkaround() { if (Util.SDK_INT <= 28) { - // Workaround for MiTV devices which have been observed broken up to API 28. + // Workaround for MiTV and MiBox devices which have been observed broken up to API 28. // https://github.com/google/ExoPlayer/issues/5169, // https://github.com/google/ExoPlayer/issues/6899. // https://github.com/google/ExoPlayer/issues/8014. + // https://github.com/google/ExoPlayer/issues/8329. switch (Util.DEVICE) { case "dangal": case "dangalUHD": case "dangalFHD": case "magnolia": case "machuca": + case "oneday": return true; default: break; // Do nothing. diff --git a/library/core/src/test/java/com/google/android/exoplayer2/ExoPlayerTest.java b/library/core/src/test/java/com/google/android/exoplayer2/ExoPlayerTest.java index 45ba914d34..e7e35902ec 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/ExoPlayerTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/ExoPlayerTest.java @@ -6943,6 +6943,7 @@ public final class ExoPlayerTest { ActionSchedule actionSchedule = new ActionSchedule.Builder(TAG) .waitForPlaybackState(Player.STATE_READY) + .waitForPendingPlayerCommands() .executeRunnable( new PlayerRunnable() { @Override diff --git a/library/core/src/test/java/com/google/android/exoplayer2/MediaPeriodQueueTest.java b/library/core/src/test/java/com/google/android/exoplayer2/MediaPeriodQueueTest.java index 20be8fe12b..0eaab52ff4 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/MediaPeriodQueueTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/MediaPeriodQueueTest.java @@ -16,7 +16,6 @@ package com.google.android.exoplayer2; import static com.google.common.truth.Truth.assertThat; -import static org.junit.Assert.assertNull; import static org.mockito.Mockito.mock; import static org.robolectric.Shadows.shadowOf; @@ -103,7 +102,8 @@ public final class MediaPeriodQueueTest { public void getNextMediaPeriodInfo_withPrerollAd_returnsCorrectMediaPeriodInfos() { setupAdTimeline(/* adGroupTimesUs...= */ 0); setAdGroupLoaded(/* adGroupIndex= */ 0); - assertNextMediaPeriodInfoIsAd(/* adGroupIndex= */ 0, /* contentPositionUs= */ C.TIME_UNSET); + assertNextMediaPeriodInfoIsAd( + /* adGroupIndex= */ 0, AD_DURATION_US, /* contentPositionUs= */ C.TIME_UNSET); advance(); assertGetNextMediaPeriodInfoReturnsContentMediaPeriod( /* periodUid= */ firstPeriodUid, @@ -128,12 +128,14 @@ public final class MediaPeriodQueueTest { /* isLastInPeriod= */ false, /* isLastInWindow= */ false, /* nextAdGroupIndex= */ 0); - // The next media period info should be null as we haven't loaded the ad yet. advance(); - assertNull(getNextMediaPeriodInfo()); + assertNextMediaPeriodInfoIsAd( + /* adGroupIndex= */ 0, + /* adDurationUs= */ C.TIME_UNSET, + /* contentPositionUs= */ FIRST_AD_START_TIME_US); setAdGroupLoaded(/* adGroupIndex= */ 0); assertNextMediaPeriodInfoIsAd( - /* adGroupIndex= */ 0, /* contentPositionUs= */ FIRST_AD_START_TIME_US); + /* adGroupIndex= */ 0, AD_DURATION_US, /* contentPositionUs= */ FIRST_AD_START_TIME_US); advance(); assertGetNextMediaPeriodInfoReturnsContentMediaPeriod( /* periodUid= */ firstPeriodUid, @@ -147,7 +149,7 @@ public final class MediaPeriodQueueTest { advance(); setAdGroupLoaded(/* adGroupIndex= */ 1); assertNextMediaPeriodInfoIsAd( - /* adGroupIndex= */ 1, /* contentPositionUs= */ SECOND_AD_START_TIME_US); + /* adGroupIndex= */ 1, AD_DURATION_US, /* contentPositionUs= */ SECOND_AD_START_TIME_US); advance(); assertGetNextMediaPeriodInfoReturnsContentMediaPeriod( /* periodUid= */ firstPeriodUid, @@ -175,7 +177,7 @@ public final class MediaPeriodQueueTest { advance(); setAdGroupLoaded(/* adGroupIndex= */ 0); assertNextMediaPeriodInfoIsAd( - /* adGroupIndex= */ 0, /* contentPositionUs= */ FIRST_AD_START_TIME_US); + /* adGroupIndex= */ 0, AD_DURATION_US, /* contentPositionUs= */ FIRST_AD_START_TIME_US); advance(); assertGetNextMediaPeriodInfoReturnsContentMediaPeriod( /* periodUid= */ firstPeriodUid, @@ -189,7 +191,7 @@ public final class MediaPeriodQueueTest { advance(); setAdGroupLoaded(/* adGroupIndex= */ 1); assertNextMediaPeriodInfoIsAd( - /* adGroupIndex= */ 1, /* contentPositionUs= */ CONTENT_DURATION_US); + /* adGroupIndex= */ 1, AD_DURATION_US, /* contentPositionUs= */ CONTENT_DURATION_US); advance(); assertGetNextMediaPeriodInfoReturnsContentMediaPeriod( /* periodUid= */ firstPeriodUid, @@ -531,7 +533,8 @@ public final class MediaPeriodQueueTest { /* isFinal= */ isLastInWindow)); } - private void assertNextMediaPeriodInfoIsAd(int adGroupIndex, long contentPositionUs) { + private void assertNextMediaPeriodInfoIsAd( + int adGroupIndex, long adDurationUs, long contentPositionUs) { assertThat(getNextMediaPeriodInfo()) .isEqualTo( new MediaPeriodInfo( @@ -543,7 +546,7 @@ public final class MediaPeriodQueueTest { /* startPositionUs= */ 0, contentPositionUs, /* endPositionUs= */ C.TIME_UNSET, - /* durationUs= */ AD_DURATION_US, + adDurationUs, /* isLastInTimelinePeriod= */ false, /* isLastInTimelineWindow= */ false, /* isFinal= */ false)); diff --git a/library/core/src/test/java/com/google/android/exoplayer2/offline/DownloadManagerTest.java b/library/core/src/test/java/com/google/android/exoplayer2/offline/DownloadManagerTest.java index d5959584ad..927d17bead 100644 --- a/library/core/src/test/java/com/google/android/exoplayer2/offline/DownloadManagerTest.java +++ b/library/core/src/test/java/com/google/android/exoplayer2/offline/DownloadManagerTest.java @@ -24,6 +24,7 @@ import androidx.annotation.Nullable; import androidx.test.core.app.ApplicationProvider; import androidx.test.ext.junit.runners.AndroidJUnit4; import com.google.android.exoplayer2.C; +import com.google.android.exoplayer2.robolectric.TestDownloadManagerListener; import com.google.android.exoplayer2.scheduler.Requirements; import com.google.android.exoplayer2.testutil.DownloadBuilder; import com.google.android.exoplayer2.testutil.DummyMainThread; @@ -582,6 +583,67 @@ public class DownloadManagerTest { assertThat(download2.state).isEqualTo(Download.STATE_REMOVING); } + @Test + public void addDownload_whilstRemovingWithStopReason_addsStartedDownload() throws Throwable { + runOnMainThread( + () -> downloadManager.addDownload(createDownloadRequest(ID1), /* stopReason= */ 1234)); + + postRemoveRequest(ID1); + FakeDownloader downloadRemover = getDownloaderAt(0); + downloadRemover.assertRemoveStarted(); + + // Re-add the download without a stop reason. + postDownloadRequest(ID1); + + downloadRemover.finish(); + + FakeDownloader downloader = getDownloaderAt(1); + downloader.finish(); + assertCompleted(ID1); + + assertDownloadIndexSize(1); + // We expect one downloader for the removal, and one for when the download was re-added. + assertDownloaderCount(2); + // The download has completed, and so is no longer current. + assertCurrentDownloadCount(0); + + Download download = postGetDownloadIndex().getDownload(ID1); + assertThat(download.state).isEqualTo(Download.STATE_COMPLETED); + assertThat(download.stopReason).isEqualTo(0); + } + + /** Test for https://github.com/google/ExoPlayer/issues/8419 */ + @Test + public void addDownloadWithStopReason_whilstRemoving_addsStoppedDownload() throws Throwable { + postDownloadRequest(ID1); + getDownloaderAt(0).finish(); + assertCompleted(ID1); + + postRemoveRequest(ID1); + FakeDownloader downloadRemover = getDownloaderAt(1); + downloadRemover.assertRemoveStarted(); + + // Re-add the download with a stop reason. + runOnMainThread( + () -> downloadManager.addDownload(createDownloadRequest(ID1), /* stopReason= */ 1234)); + + downloadRemover.finish(); + downloadManagerListener.blockUntilIdle(); + + assertDownloadIndexSize(1); + // We expect one downloader for the initial download, and one for the removal. A downloader + // should not be created when the download is re-added, since a stop reason is specified. + assertDownloaderCount(2); + // The download isn't completed, and is therefore still current. + assertCurrentDownloadCount(1); + + List downloads = postGetCurrentDownloads(); + Download download = downloads.get(0); + assertThat(download.request.id).isEqualTo(ID1); + assertThat(download.state).isEqualTo(Download.STATE_STOPPED); + assertThat(download.stopReason).isEqualTo(1234); + } + @Test public void mergeRequest_removing_becomesRestarting() { DownloadRequest downloadRequest = createDownloadRequest(ID1); diff --git a/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/mkv/MatroskaExtractor.java b/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/mkv/MatroskaExtractor.java index c8f4cadcb1..f4022fe780 100644 --- a/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/mkv/MatroskaExtractor.java +++ b/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/mkv/MatroskaExtractor.java @@ -1307,6 +1307,15 @@ public class MatroskaExtractor implements Extractor { Log.w(TAG, "Skipping subtitle sample with no duration."); } else { setSubtitleEndTime(track.codecId, blockDurationUs, subtitleSample.getData()); + // The Matroska spec doesn't clearly define whether subtitle samples are null-terminated + // or the sample should instead be sized precisely. We truncate the sample at a null-byte + // to gracefully handle null-terminated strings followed by garbage bytes. + for (int i = subtitleSample.getPosition(); i < subtitleSample.limit(); i++) { + if (subtitleSample.getData()[i] == 0) { + subtitleSample.setLimit(i); + break; + } + } // Note: If we ever want to support DRM protected subtitles then we'll need to output the // appropriate encryption data here. track.output.sampleData(subtitleSample, subtitleSample.limit()); diff --git a/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/mp4/Sniffer.java b/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/mp4/Sniffer.java index 00acb29906..72ce336b73 100644 --- a/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/mp4/Sniffer.java +++ b/library/extractor/src/main/java/com/google/android/exoplayer2/extractor/mp4/Sniffer.java @@ -37,6 +37,7 @@ import java.io.IOException; 0x69736f34, // iso4 0x69736f35, // iso5 0x69736f36, // iso6 + 0x69736f39, // iso9 0x61766331, // avc1 0x68766331, // hvc1 0x68657631, // hev1 diff --git a/library/extractor/src/test/java/com/google/android/exoplayer2/extractor/mkv/MatroskaExtractorTest.java b/library/extractor/src/test/java/com/google/android/exoplayer2/extractor/mkv/MatroskaExtractorTest.java index 8e22aace8a..45c0bd1c9f 100644 --- a/library/extractor/src/test/java/com/google/android/exoplayer2/extractor/mkv/MatroskaExtractorTest.java +++ b/library/extractor/src/test/java/com/google/android/exoplayer2/extractor/mkv/MatroskaExtractorTest.java @@ -46,6 +46,27 @@ public final class MatroskaExtractorTest { MatroskaExtractor::new, "media/mkv/sample_with_srt.mkv", simulationConfig); } + @Test + public void mkvSample_withNullTerminatedSubripSubtitles() throws Exception { + ExtractorAsserts.assertBehavior( + MatroskaExtractor::new, "media/mkv/sample_with_null_terminated_srt.mkv", simulationConfig); + } + + @Test + public void mkvSample_withSsaSubtitles() throws Exception { + ExtractorAsserts.assertBehavior( + MatroskaExtractor::new, "media/mkv/sample_with_ssa_subtitles.mkv", simulationConfig); + } + + // https://github.com/google/ExoPlayer/pull/8265 + @Test + public void mkvSample_withNullTerminatedSsaSubtitles() throws Exception { + ExtractorAsserts.assertBehavior( + MatroskaExtractor::new, + "media/mkv/sample_with_null_terminated_ssa_subtitles.mkv", + simulationConfig); + } + @Test public void mkvSample_withHtcRotationInfoInTrackName() throws Exception { ExtractorAsserts.assertBehavior( diff --git a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/BundledHlsMediaChunkExtractor.java b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/BundledHlsMediaChunkExtractor.java index 78fc9ae732..3311d263c6 100644 --- a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/BundledHlsMediaChunkExtractor.java +++ b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/BundledHlsMediaChunkExtractor.java @@ -101,4 +101,9 @@ public final class BundledHlsMediaChunkExtractor implements HlsMediaChunkExtract return new BundledHlsMediaChunkExtractor( newExtractorInstance, masterPlaylistFormat, timestampAdjuster); } + + @Override + public void onTruncatedSegmentParsed() { + extractor.seek(/* position= */ 0, /* timeUs= */ 0); + } } diff --git a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaChunk.java b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaChunk.java index 9994ede1cf..8de69a68d1 100644 --- a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaChunk.java +++ b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaChunk.java @@ -403,6 +403,14 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; } try { while (!loadCanceled && extractor.read(input)) {} + } catch (EOFException e) { + if ((trackFormat.roleFlags & C.ROLE_FLAG_TRICK_PLAY) != 0) { + // See onTruncatedSegmentParsed's javadoc for more info on why we are swallowing the EOF + // exception for trick play tracks. + extractor.onTruncatedSegmentParsed(); + } else { + throw e; + } } finally { nextLoadPosition = (int) (input.getPosition() - dataSpec.position); } diff --git a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaChunkExtractor.java b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaChunkExtractor.java index 0ca5c5d0ad..084a3450ba 100644 --- a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaChunkExtractor.java +++ b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/HlsMediaChunkExtractor.java @@ -59,4 +59,15 @@ public interface HlsMediaChunkExtractor { * instances that are not {@link #isReusable() reusable}. */ HlsMediaChunkExtractor recreate(); + + /** + * Resets the sample parsing state. + * + *

Resetting the parsing state allows support for Fragmented MP4 EXT-X-I-FRAME-STREAM-INF + * segments. EXT-X-I-FRAME-STREAM-INF segments are truncated to include only a leading key frame. + * After parsing said keyframe, an extractor may reach an unexpected end of file. By resetting its + * state, we can continue feeding samples from the following segments to the extractor. See #7512 for context. + */ + void onTruncatedSegmentParsed(); } diff --git a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistParser.java b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistParser.java index fd6efbf445..e6bdb0e03a 100644 --- a/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistParser.java +++ b/library/hls/src/main/java/com/google/android/exoplayer2/source/hls/playlist/HlsPlaylistParser.java @@ -34,6 +34,7 @@ import com.google.android.exoplayer2.source.hls.playlist.HlsMasterPlaylist.Varia import com.google.android.exoplayer2.source.hls.playlist.HlsMediaPlaylist.Segment; import com.google.android.exoplayer2.upstream.ParsingLoadable; import com.google.android.exoplayer2.util.Assertions; +import com.google.android.exoplayer2.util.Log; import com.google.android.exoplayer2.util.MimeTypes; import com.google.android.exoplayer2.util.UriUtil; import com.google.android.exoplayer2.util.Util; @@ -61,6 +62,8 @@ import org.checkerframework.checker.nullness.qual.PolyNull; */ public final class HlsPlaylistParser implements ParsingLoadable.Parser { + private static final String LOG_TAG = "HlsPlaylistParser"; + private static final String PLAYLIST_HEADER = "#EXTM3U"; private static final String TAG_PREFIX = "#EXT"; @@ -480,7 +483,11 @@ public final class HlsPlaylistParser implements ParsingLoadable.ParserDefault: {@code never} * *

  • {@code resize_mode} - Controls how video and album art is resized within the view. - * Valid values are {@code fit}, {@code fixed_width}, {@code fixed_height} and {@code fill}. + * Valid values are {@code fit}, {@code fixed_width}, {@code fixed_height}, {@code fill} and + * {@code zoom}. *
      *
    • Corresponding method: {@link #setResizeMode(int)} *
    • Default: {@code fit} diff --git a/library/ui/src/main/java/com/google/android/exoplayer2/ui/StyledPlayerControlView.java b/library/ui/src/main/java/com/google/android/exoplayer2/ui/StyledPlayerControlView.java index 3cff0ef3cb..ab09454c95 100644 --- a/library/ui/src/main/java/com/google/android/exoplayer2/ui/StyledPlayerControlView.java +++ b/library/ui/src/main/java/com/google/android/exoplayer2/ui/StyledPlayerControlView.java @@ -21,7 +21,9 @@ import android.annotation.SuppressLint; import android.content.Context; import android.content.res.Resources; import android.content.res.TypedArray; +import android.graphics.Color; import android.graphics.Typeface; +import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; import android.os.Looper; import android.util.AttributeSet; @@ -673,6 +675,11 @@ public class StyledPlayerControlView extends FrameLayout { settingsView.setLayoutManager(new LinearLayoutManager(getContext())); settingsWindow = new PopupWindow(settingsView, LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, true); + if (Util.SDK_INT < 23) { + // Work around issue where tapping outside of the menu area or pressing the back button + // doesn't dismiss the menu as expected. See: https://github.com/google/ExoPlayer/issues/8272. + settingsWindow.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT)); + } settingsWindow.setOnDismissListener(componentListener); needToHideBars = true; diff --git a/library/ui/src/main/java/com/google/android/exoplayer2/ui/StyledPlayerView.java b/library/ui/src/main/java/com/google/android/exoplayer2/ui/StyledPlayerView.java index 4ae1b32215..81df78536d 100644 --- a/library/ui/src/main/java/com/google/android/exoplayer2/ui/StyledPlayerView.java +++ b/library/ui/src/main/java/com/google/android/exoplayer2/ui/StyledPlayerView.java @@ -133,7 +133,8 @@ import org.checkerframework.checker.nullness.qual.RequiresNonNull; *
    • Default: {@code never} *
    *
  • {@code resize_mode} - Controls how video and album art is resized within the view. - * Valid values are {@code fit}, {@code fixed_width}, {@code fixed_height} and {@code fill}. + * Valid values are {@code fit}, {@code fixed_width}, {@code fixed_height}, {@code fill} and + * {@code zoom}. *
      *
    • Corresponding method: {@link #setResizeMode(int)} *
    • Default: {@code fit} diff --git a/testdata/src/test/assets/extractordumps/mkv/sample_with_null_terminated_srt.mkv.0.dump b/testdata/src/test/assets/extractordumps/mkv/sample_with_null_terminated_srt.mkv.0.dump new file mode 100644 index 0000000000..ccf35b8c27 --- /dev/null +++ b/testdata/src/test/assets/extractordumps/mkv/sample_with_null_terminated_srt.mkv.0.dump @@ -0,0 +1,281 @@ +seekMap: + isSeekable = true + duration = 1234000 + getPosition(0) = [[timeUs=0, position=1163]] + getPosition(1) = [[timeUs=0, position=1163]] + getPosition(617000) = [[timeUs=0, position=1163]] + getPosition(1234000) = [[timeUs=0, position=1163]] +numberOfTracks = 3 +track 1: + total output bytes = 89502 + sample count = 30 + format 0: + id = 1 + sampleMimeType = video/avc + width = 1080 + height = 720 + selectionFlags = 1 + language = und + initializationData: + data = length 30, hash F6F3D010 + data = length 10, hash 7A0D0F2B + sample 0: + time = 0 + flags = 1 + data = length 36477, hash F0F36CFE + sample 1: + time = 67000 + flags = 0 + data = length 5341, hash 40B85E2 + sample 2: + time = 33000 + flags = 0 + data = length 596, hash 357B4D92 + sample 3: + time = 200000 + flags = 0 + data = length 7704, hash A39EDA06 + sample 4: + time = 133000 + flags = 0 + data = length 989, hash 2813C72D + sample 5: + time = 100000 + flags = 0 + data = length 721, hash C50D1C73 + sample 6: + time = 167000 + flags = 0 + data = length 519, hash 65FE1911 + sample 7: + time = 333000 + flags = 0 + data = length 6160, hash E1CAC0EC + sample 8: + time = 267000 + flags = 0 + data = length 953, hash 7160C661 + sample 9: + time = 233000 + flags = 0 + data = length 620, hash 7A7AE07C + sample 10: + time = 300000 + flags = 0 + data = length 405, hash 5CC7F4E7 + sample 11: + time = 433000 + flags = 0 + data = length 4852, hash 9DB6979D + sample 12: + time = 400000 + flags = 0 + data = length 547, hash E31A6979 + sample 13: + time = 367000 + flags = 0 + data = length 570, hash FEC40D00 + sample 14: + time = 567000 + flags = 0 + data = length 5525, hash 7C478F7E + sample 15: + time = 500000 + flags = 0 + data = length 1082, hash DA07059A + sample 16: + time = 467000 + flags = 0 + data = length 807, hash 93478E6B + sample 17: + time = 533000 + flags = 0 + data = length 744, hash 9A8E6026 + sample 18: + time = 700000 + flags = 0 + data = length 4732, hash C73B23C0 + sample 19: + time = 633000 + flags = 0 + data = length 1004, hash 8A19A228 + sample 20: + time = 600000 + flags = 0 + data = length 794, hash 8126022C + sample 21: + time = 667000 + flags = 0 + data = length 645, hash F08300E5 + sample 22: + time = 833000 + flags = 0 + data = length 2684, hash 727FE378 + sample 23: + time = 767000 + flags = 0 + data = length 787, hash 419A7821 + sample 24: + time = 733000 + flags = 0 + data = length 649, hash 5C159346 + sample 25: + time = 800000 + flags = 0 + data = length 509, hash F912D655 + sample 26: + time = 967000 + flags = 0 + data = length 1226, hash 29815C21 + sample 27: + time = 900000 + flags = 0 + data = length 898, hash D997AD0A + sample 28: + time = 867000 + flags = 0 + data = length 476, hash A0423645 + sample 29: + time = 933000 + flags = 0 + data = length 486, hash DDF32CBB +track 2: + total output bytes = 12120 + sample count = 29 + format 0: + id = 2 + sampleMimeType = audio/ac3 + channelCount = 1 + sampleRate = 44100 + selectionFlags = 1 + language = und + sample 0: + time = 62000 + flags = 1 + data = length 416, hash 211F2286 + sample 1: + time = 97000 + flags = 1 + data = length 418, hash 77425A86 + sample 2: + time = 131000 + flags = 1 + data = length 418, hash A0FE5CA1 + sample 3: + time = 166000 + flags = 1 + data = length 418, hash 2309B066 + sample 4: + time = 201000 + flags = 1 + data = length 418, hash 928A653B + sample 5: + time = 236000 + flags = 1 + data = length 418, hash 3422F0CB + sample 6: + time = 270000 + flags = 1 + data = length 418, hash EFF43D5B + sample 7: + time = 306000 + flags = 1 + data = length 418, hash FC8093C7 + sample 8: + time = 341000 + flags = 1 + data = length 418, hash CCC08A16 + sample 9: + time = 376000 + flags = 1 + data = length 418, hash 2A6EE863 + sample 10: + time = 410000 + flags = 1 + data = length 418, hash D69A9251 + sample 11: + time = 445000 + flags = 1 + data = length 418, hash BCFB758D + sample 12: + time = 480000 + flags = 1 + data = length 418, hash 11B66799 + sample 13: + time = 514000 + flags = 1 + data = length 418, hash C824D392 + sample 14: + time = 550000 + flags = 1 + data = length 418, hash C167D872 + sample 15: + time = 585000 + flags = 1 + data = length 418, hash 4221C855 + sample 16: + time = 620000 + flags = 1 + data = length 418, hash 4D4FF934 + sample 17: + time = 654000 + flags = 1 + data = length 418, hash 984AA025 + sample 18: + time = 690000 + flags = 1 + data = length 418, hash BB788B46 + sample 19: + time = 724000 + flags = 1 + data = length 418, hash 9EFBFD97 + sample 20: + time = 759000 + flags = 1 + data = length 418, hash DF1A460C + sample 21: + time = 793000 + flags = 1 + data = length 418, hash 2BDB56A + sample 22: + time = 829000 + flags = 1 + data = length 418, hash CA230060 + sample 23: + time = 864000 + flags = 1 + data = length 418, hash D2F19F41 + sample 24: + time = 898000 + flags = 1 + data = length 418, hash AF392D79 + sample 25: + time = 932000 + flags = 1 + data = length 418, hash C5D7F2A3 + sample 26: + time = 968000 + flags = 1 + data = length 418, hash 733A35AE + sample 27: + time = 1002000 + flags = 1 + data = length 418, hash DE46E5D3 + sample 28: + time = 1037000 + flags = 1 + data = length 418, hash 56AB8D37 +track 3: + total output bytes = 49 + sample count = 1 + format 0: + id = 3 + sampleMimeType = application/x-subrip + selectionFlags = 1 + language = en + label = Subs Label + sample 0: + time = 0 + flags = 1 + data = length 49, hash DE7F89EF +tracksEnded = true diff --git a/testdata/src/test/assets/extractordumps/mkv/sample_with_null_terminated_srt.mkv.1.dump b/testdata/src/test/assets/extractordumps/mkv/sample_with_null_terminated_srt.mkv.1.dump new file mode 100644 index 0000000000..ccf35b8c27 --- /dev/null +++ b/testdata/src/test/assets/extractordumps/mkv/sample_with_null_terminated_srt.mkv.1.dump @@ -0,0 +1,281 @@ +seekMap: + isSeekable = true + duration = 1234000 + getPosition(0) = [[timeUs=0, position=1163]] + getPosition(1) = [[timeUs=0, position=1163]] + getPosition(617000) = [[timeUs=0, position=1163]] + getPosition(1234000) = [[timeUs=0, position=1163]] +numberOfTracks = 3 +track 1: + total output bytes = 89502 + sample count = 30 + format 0: + id = 1 + sampleMimeType = video/avc + width = 1080 + height = 720 + selectionFlags = 1 + language = und + initializationData: + data = length 30, hash F6F3D010 + data = length 10, hash 7A0D0F2B + sample 0: + time = 0 + flags = 1 + data = length 36477, hash F0F36CFE + sample 1: + time = 67000 + flags = 0 + data = length 5341, hash 40B85E2 + sample 2: + time = 33000 + flags = 0 + data = length 596, hash 357B4D92 + sample 3: + time = 200000 + flags = 0 + data = length 7704, hash A39EDA06 + sample 4: + time = 133000 + flags = 0 + data = length 989, hash 2813C72D + sample 5: + time = 100000 + flags = 0 + data = length 721, hash C50D1C73 + sample 6: + time = 167000 + flags = 0 + data = length 519, hash 65FE1911 + sample 7: + time = 333000 + flags = 0 + data = length 6160, hash E1CAC0EC + sample 8: + time = 267000 + flags = 0 + data = length 953, hash 7160C661 + sample 9: + time = 233000 + flags = 0 + data = length 620, hash 7A7AE07C + sample 10: + time = 300000 + flags = 0 + data = length 405, hash 5CC7F4E7 + sample 11: + time = 433000 + flags = 0 + data = length 4852, hash 9DB6979D + sample 12: + time = 400000 + flags = 0 + data = length 547, hash E31A6979 + sample 13: + time = 367000 + flags = 0 + data = length 570, hash FEC40D00 + sample 14: + time = 567000 + flags = 0 + data = length 5525, hash 7C478F7E + sample 15: + time = 500000 + flags = 0 + data = length 1082, hash DA07059A + sample 16: + time = 467000 + flags = 0 + data = length 807, hash 93478E6B + sample 17: + time = 533000 + flags = 0 + data = length 744, hash 9A8E6026 + sample 18: + time = 700000 + flags = 0 + data = length 4732, hash C73B23C0 + sample 19: + time = 633000 + flags = 0 + data = length 1004, hash 8A19A228 + sample 20: + time = 600000 + flags = 0 + data = length 794, hash 8126022C + sample 21: + time = 667000 + flags = 0 + data = length 645, hash F08300E5 + sample 22: + time = 833000 + flags = 0 + data = length 2684, hash 727FE378 + sample 23: + time = 767000 + flags = 0 + data = length 787, hash 419A7821 + sample 24: + time = 733000 + flags = 0 + data = length 649, hash 5C159346 + sample 25: + time = 800000 + flags = 0 + data = length 509, hash F912D655 + sample 26: + time = 967000 + flags = 0 + data = length 1226, hash 29815C21 + sample 27: + time = 900000 + flags = 0 + data = length 898, hash D997AD0A + sample 28: + time = 867000 + flags = 0 + data = length 476, hash A0423645 + sample 29: + time = 933000 + flags = 0 + data = length 486, hash DDF32CBB +track 2: + total output bytes = 12120 + sample count = 29 + format 0: + id = 2 + sampleMimeType = audio/ac3 + channelCount = 1 + sampleRate = 44100 + selectionFlags = 1 + language = und + sample 0: + time = 62000 + flags = 1 + data = length 416, hash 211F2286 + sample 1: + time = 97000 + flags = 1 + data = length 418, hash 77425A86 + sample 2: + time = 131000 + flags = 1 + data = length 418, hash A0FE5CA1 + sample 3: + time = 166000 + flags = 1 + data = length 418, hash 2309B066 + sample 4: + time = 201000 + flags = 1 + data = length 418, hash 928A653B + sample 5: + time = 236000 + flags = 1 + data = length 418, hash 3422F0CB + sample 6: + time = 270000 + flags = 1 + data = length 418, hash EFF43D5B + sample 7: + time = 306000 + flags = 1 + data = length 418, hash FC8093C7 + sample 8: + time = 341000 + flags = 1 + data = length 418, hash CCC08A16 + sample 9: + time = 376000 + flags = 1 + data = length 418, hash 2A6EE863 + sample 10: + time = 410000 + flags = 1 + data = length 418, hash D69A9251 + sample 11: + time = 445000 + flags = 1 + data = length 418, hash BCFB758D + sample 12: + time = 480000 + flags = 1 + data = length 418, hash 11B66799 + sample 13: + time = 514000 + flags = 1 + data = length 418, hash C824D392 + sample 14: + time = 550000 + flags = 1 + data = length 418, hash C167D872 + sample 15: + time = 585000 + flags = 1 + data = length 418, hash 4221C855 + sample 16: + time = 620000 + flags = 1 + data = length 418, hash 4D4FF934 + sample 17: + time = 654000 + flags = 1 + data = length 418, hash 984AA025 + sample 18: + time = 690000 + flags = 1 + data = length 418, hash BB788B46 + sample 19: + time = 724000 + flags = 1 + data = length 418, hash 9EFBFD97 + sample 20: + time = 759000 + flags = 1 + data = length 418, hash DF1A460C + sample 21: + time = 793000 + flags = 1 + data = length 418, hash 2BDB56A + sample 22: + time = 829000 + flags = 1 + data = length 418, hash CA230060 + sample 23: + time = 864000 + flags = 1 + data = length 418, hash D2F19F41 + sample 24: + time = 898000 + flags = 1 + data = length 418, hash AF392D79 + sample 25: + time = 932000 + flags = 1 + data = length 418, hash C5D7F2A3 + sample 26: + time = 968000 + flags = 1 + data = length 418, hash 733A35AE + sample 27: + time = 1002000 + flags = 1 + data = length 418, hash DE46E5D3 + sample 28: + time = 1037000 + flags = 1 + data = length 418, hash 56AB8D37 +track 3: + total output bytes = 49 + sample count = 1 + format 0: + id = 3 + sampleMimeType = application/x-subrip + selectionFlags = 1 + language = en + label = Subs Label + sample 0: + time = 0 + flags = 1 + data = length 49, hash DE7F89EF +tracksEnded = true diff --git a/testdata/src/test/assets/extractordumps/mkv/sample_with_null_terminated_srt.mkv.2.dump b/testdata/src/test/assets/extractordumps/mkv/sample_with_null_terminated_srt.mkv.2.dump new file mode 100644 index 0000000000..ccf35b8c27 --- /dev/null +++ b/testdata/src/test/assets/extractordumps/mkv/sample_with_null_terminated_srt.mkv.2.dump @@ -0,0 +1,281 @@ +seekMap: + isSeekable = true + duration = 1234000 + getPosition(0) = [[timeUs=0, position=1163]] + getPosition(1) = [[timeUs=0, position=1163]] + getPosition(617000) = [[timeUs=0, position=1163]] + getPosition(1234000) = [[timeUs=0, position=1163]] +numberOfTracks = 3 +track 1: + total output bytes = 89502 + sample count = 30 + format 0: + id = 1 + sampleMimeType = video/avc + width = 1080 + height = 720 + selectionFlags = 1 + language = und + initializationData: + data = length 30, hash F6F3D010 + data = length 10, hash 7A0D0F2B + sample 0: + time = 0 + flags = 1 + data = length 36477, hash F0F36CFE + sample 1: + time = 67000 + flags = 0 + data = length 5341, hash 40B85E2 + sample 2: + time = 33000 + flags = 0 + data = length 596, hash 357B4D92 + sample 3: + time = 200000 + flags = 0 + data = length 7704, hash A39EDA06 + sample 4: + time = 133000 + flags = 0 + data = length 989, hash 2813C72D + sample 5: + time = 100000 + flags = 0 + data = length 721, hash C50D1C73 + sample 6: + time = 167000 + flags = 0 + data = length 519, hash 65FE1911 + sample 7: + time = 333000 + flags = 0 + data = length 6160, hash E1CAC0EC + sample 8: + time = 267000 + flags = 0 + data = length 953, hash 7160C661 + sample 9: + time = 233000 + flags = 0 + data = length 620, hash 7A7AE07C + sample 10: + time = 300000 + flags = 0 + data = length 405, hash 5CC7F4E7 + sample 11: + time = 433000 + flags = 0 + data = length 4852, hash 9DB6979D + sample 12: + time = 400000 + flags = 0 + data = length 547, hash E31A6979 + sample 13: + time = 367000 + flags = 0 + data = length 570, hash FEC40D00 + sample 14: + time = 567000 + flags = 0 + data = length 5525, hash 7C478F7E + sample 15: + time = 500000 + flags = 0 + data = length 1082, hash DA07059A + sample 16: + time = 467000 + flags = 0 + data = length 807, hash 93478E6B + sample 17: + time = 533000 + flags = 0 + data = length 744, hash 9A8E6026 + sample 18: + time = 700000 + flags = 0 + data = length 4732, hash C73B23C0 + sample 19: + time = 633000 + flags = 0 + data = length 1004, hash 8A19A228 + sample 20: + time = 600000 + flags = 0 + data = length 794, hash 8126022C + sample 21: + time = 667000 + flags = 0 + data = length 645, hash F08300E5 + sample 22: + time = 833000 + flags = 0 + data = length 2684, hash 727FE378 + sample 23: + time = 767000 + flags = 0 + data = length 787, hash 419A7821 + sample 24: + time = 733000 + flags = 0 + data = length 649, hash 5C159346 + sample 25: + time = 800000 + flags = 0 + data = length 509, hash F912D655 + sample 26: + time = 967000 + flags = 0 + data = length 1226, hash 29815C21 + sample 27: + time = 900000 + flags = 0 + data = length 898, hash D997AD0A + sample 28: + time = 867000 + flags = 0 + data = length 476, hash A0423645 + sample 29: + time = 933000 + flags = 0 + data = length 486, hash DDF32CBB +track 2: + total output bytes = 12120 + sample count = 29 + format 0: + id = 2 + sampleMimeType = audio/ac3 + channelCount = 1 + sampleRate = 44100 + selectionFlags = 1 + language = und + sample 0: + time = 62000 + flags = 1 + data = length 416, hash 211F2286 + sample 1: + time = 97000 + flags = 1 + data = length 418, hash 77425A86 + sample 2: + time = 131000 + flags = 1 + data = length 418, hash A0FE5CA1 + sample 3: + time = 166000 + flags = 1 + data = length 418, hash 2309B066 + sample 4: + time = 201000 + flags = 1 + data = length 418, hash 928A653B + sample 5: + time = 236000 + flags = 1 + data = length 418, hash 3422F0CB + sample 6: + time = 270000 + flags = 1 + data = length 418, hash EFF43D5B + sample 7: + time = 306000 + flags = 1 + data = length 418, hash FC8093C7 + sample 8: + time = 341000 + flags = 1 + data = length 418, hash CCC08A16 + sample 9: + time = 376000 + flags = 1 + data = length 418, hash 2A6EE863 + sample 10: + time = 410000 + flags = 1 + data = length 418, hash D69A9251 + sample 11: + time = 445000 + flags = 1 + data = length 418, hash BCFB758D + sample 12: + time = 480000 + flags = 1 + data = length 418, hash 11B66799 + sample 13: + time = 514000 + flags = 1 + data = length 418, hash C824D392 + sample 14: + time = 550000 + flags = 1 + data = length 418, hash C167D872 + sample 15: + time = 585000 + flags = 1 + data = length 418, hash 4221C855 + sample 16: + time = 620000 + flags = 1 + data = length 418, hash 4D4FF934 + sample 17: + time = 654000 + flags = 1 + data = length 418, hash 984AA025 + sample 18: + time = 690000 + flags = 1 + data = length 418, hash BB788B46 + sample 19: + time = 724000 + flags = 1 + data = length 418, hash 9EFBFD97 + sample 20: + time = 759000 + flags = 1 + data = length 418, hash DF1A460C + sample 21: + time = 793000 + flags = 1 + data = length 418, hash 2BDB56A + sample 22: + time = 829000 + flags = 1 + data = length 418, hash CA230060 + sample 23: + time = 864000 + flags = 1 + data = length 418, hash D2F19F41 + sample 24: + time = 898000 + flags = 1 + data = length 418, hash AF392D79 + sample 25: + time = 932000 + flags = 1 + data = length 418, hash C5D7F2A3 + sample 26: + time = 968000 + flags = 1 + data = length 418, hash 733A35AE + sample 27: + time = 1002000 + flags = 1 + data = length 418, hash DE46E5D3 + sample 28: + time = 1037000 + flags = 1 + data = length 418, hash 56AB8D37 +track 3: + total output bytes = 49 + sample count = 1 + format 0: + id = 3 + sampleMimeType = application/x-subrip + selectionFlags = 1 + language = en + label = Subs Label + sample 0: + time = 0 + flags = 1 + data = length 49, hash DE7F89EF +tracksEnded = true diff --git a/testdata/src/test/assets/extractordumps/mkv/sample_with_null_terminated_srt.mkv.3.dump b/testdata/src/test/assets/extractordumps/mkv/sample_with_null_terminated_srt.mkv.3.dump new file mode 100644 index 0000000000..ccf35b8c27 --- /dev/null +++ b/testdata/src/test/assets/extractordumps/mkv/sample_with_null_terminated_srt.mkv.3.dump @@ -0,0 +1,281 @@ +seekMap: + isSeekable = true + duration = 1234000 + getPosition(0) = [[timeUs=0, position=1163]] + getPosition(1) = [[timeUs=0, position=1163]] + getPosition(617000) = [[timeUs=0, position=1163]] + getPosition(1234000) = [[timeUs=0, position=1163]] +numberOfTracks = 3 +track 1: + total output bytes = 89502 + sample count = 30 + format 0: + id = 1 + sampleMimeType = video/avc + width = 1080 + height = 720 + selectionFlags = 1 + language = und + initializationData: + data = length 30, hash F6F3D010 + data = length 10, hash 7A0D0F2B + sample 0: + time = 0 + flags = 1 + data = length 36477, hash F0F36CFE + sample 1: + time = 67000 + flags = 0 + data = length 5341, hash 40B85E2 + sample 2: + time = 33000 + flags = 0 + data = length 596, hash 357B4D92 + sample 3: + time = 200000 + flags = 0 + data = length 7704, hash A39EDA06 + sample 4: + time = 133000 + flags = 0 + data = length 989, hash 2813C72D + sample 5: + time = 100000 + flags = 0 + data = length 721, hash C50D1C73 + sample 6: + time = 167000 + flags = 0 + data = length 519, hash 65FE1911 + sample 7: + time = 333000 + flags = 0 + data = length 6160, hash E1CAC0EC + sample 8: + time = 267000 + flags = 0 + data = length 953, hash 7160C661 + sample 9: + time = 233000 + flags = 0 + data = length 620, hash 7A7AE07C + sample 10: + time = 300000 + flags = 0 + data = length 405, hash 5CC7F4E7 + sample 11: + time = 433000 + flags = 0 + data = length 4852, hash 9DB6979D + sample 12: + time = 400000 + flags = 0 + data = length 547, hash E31A6979 + sample 13: + time = 367000 + flags = 0 + data = length 570, hash FEC40D00 + sample 14: + time = 567000 + flags = 0 + data = length 5525, hash 7C478F7E + sample 15: + time = 500000 + flags = 0 + data = length 1082, hash DA07059A + sample 16: + time = 467000 + flags = 0 + data = length 807, hash 93478E6B + sample 17: + time = 533000 + flags = 0 + data = length 744, hash 9A8E6026 + sample 18: + time = 700000 + flags = 0 + data = length 4732, hash C73B23C0 + sample 19: + time = 633000 + flags = 0 + data = length 1004, hash 8A19A228 + sample 20: + time = 600000 + flags = 0 + data = length 794, hash 8126022C + sample 21: + time = 667000 + flags = 0 + data = length 645, hash F08300E5 + sample 22: + time = 833000 + flags = 0 + data = length 2684, hash 727FE378 + sample 23: + time = 767000 + flags = 0 + data = length 787, hash 419A7821 + sample 24: + time = 733000 + flags = 0 + data = length 649, hash 5C159346 + sample 25: + time = 800000 + flags = 0 + data = length 509, hash F912D655 + sample 26: + time = 967000 + flags = 0 + data = length 1226, hash 29815C21 + sample 27: + time = 900000 + flags = 0 + data = length 898, hash D997AD0A + sample 28: + time = 867000 + flags = 0 + data = length 476, hash A0423645 + sample 29: + time = 933000 + flags = 0 + data = length 486, hash DDF32CBB +track 2: + total output bytes = 12120 + sample count = 29 + format 0: + id = 2 + sampleMimeType = audio/ac3 + channelCount = 1 + sampleRate = 44100 + selectionFlags = 1 + language = und + sample 0: + time = 62000 + flags = 1 + data = length 416, hash 211F2286 + sample 1: + time = 97000 + flags = 1 + data = length 418, hash 77425A86 + sample 2: + time = 131000 + flags = 1 + data = length 418, hash A0FE5CA1 + sample 3: + time = 166000 + flags = 1 + data = length 418, hash 2309B066 + sample 4: + time = 201000 + flags = 1 + data = length 418, hash 928A653B + sample 5: + time = 236000 + flags = 1 + data = length 418, hash 3422F0CB + sample 6: + time = 270000 + flags = 1 + data = length 418, hash EFF43D5B + sample 7: + time = 306000 + flags = 1 + data = length 418, hash FC8093C7 + sample 8: + time = 341000 + flags = 1 + data = length 418, hash CCC08A16 + sample 9: + time = 376000 + flags = 1 + data = length 418, hash 2A6EE863 + sample 10: + time = 410000 + flags = 1 + data = length 418, hash D69A9251 + sample 11: + time = 445000 + flags = 1 + data = length 418, hash BCFB758D + sample 12: + time = 480000 + flags = 1 + data = length 418, hash 11B66799 + sample 13: + time = 514000 + flags = 1 + data = length 418, hash C824D392 + sample 14: + time = 550000 + flags = 1 + data = length 418, hash C167D872 + sample 15: + time = 585000 + flags = 1 + data = length 418, hash 4221C855 + sample 16: + time = 620000 + flags = 1 + data = length 418, hash 4D4FF934 + sample 17: + time = 654000 + flags = 1 + data = length 418, hash 984AA025 + sample 18: + time = 690000 + flags = 1 + data = length 418, hash BB788B46 + sample 19: + time = 724000 + flags = 1 + data = length 418, hash 9EFBFD97 + sample 20: + time = 759000 + flags = 1 + data = length 418, hash DF1A460C + sample 21: + time = 793000 + flags = 1 + data = length 418, hash 2BDB56A + sample 22: + time = 829000 + flags = 1 + data = length 418, hash CA230060 + sample 23: + time = 864000 + flags = 1 + data = length 418, hash D2F19F41 + sample 24: + time = 898000 + flags = 1 + data = length 418, hash AF392D79 + sample 25: + time = 932000 + flags = 1 + data = length 418, hash C5D7F2A3 + sample 26: + time = 968000 + flags = 1 + data = length 418, hash 733A35AE + sample 27: + time = 1002000 + flags = 1 + data = length 418, hash DE46E5D3 + sample 28: + time = 1037000 + flags = 1 + data = length 418, hash 56AB8D37 +track 3: + total output bytes = 49 + sample count = 1 + format 0: + id = 3 + sampleMimeType = application/x-subrip + selectionFlags = 1 + language = en + label = Subs Label + sample 0: + time = 0 + flags = 1 + data = length 49, hash DE7F89EF +tracksEnded = true diff --git a/testdata/src/test/assets/extractordumps/mkv/sample_with_null_terminated_srt.mkv.unknown_length.dump b/testdata/src/test/assets/extractordumps/mkv/sample_with_null_terminated_srt.mkv.unknown_length.dump new file mode 100644 index 0000000000..ccf35b8c27 --- /dev/null +++ b/testdata/src/test/assets/extractordumps/mkv/sample_with_null_terminated_srt.mkv.unknown_length.dump @@ -0,0 +1,281 @@ +seekMap: + isSeekable = true + duration = 1234000 + getPosition(0) = [[timeUs=0, position=1163]] + getPosition(1) = [[timeUs=0, position=1163]] + getPosition(617000) = [[timeUs=0, position=1163]] + getPosition(1234000) = [[timeUs=0, position=1163]] +numberOfTracks = 3 +track 1: + total output bytes = 89502 + sample count = 30 + format 0: + id = 1 + sampleMimeType = video/avc + width = 1080 + height = 720 + selectionFlags = 1 + language = und + initializationData: + data = length 30, hash F6F3D010 + data = length 10, hash 7A0D0F2B + sample 0: + time = 0 + flags = 1 + data = length 36477, hash F0F36CFE + sample 1: + time = 67000 + flags = 0 + data = length 5341, hash 40B85E2 + sample 2: + time = 33000 + flags = 0 + data = length 596, hash 357B4D92 + sample 3: + time = 200000 + flags = 0 + data = length 7704, hash A39EDA06 + sample 4: + time = 133000 + flags = 0 + data = length 989, hash 2813C72D + sample 5: + time = 100000 + flags = 0 + data = length 721, hash C50D1C73 + sample 6: + time = 167000 + flags = 0 + data = length 519, hash 65FE1911 + sample 7: + time = 333000 + flags = 0 + data = length 6160, hash E1CAC0EC + sample 8: + time = 267000 + flags = 0 + data = length 953, hash 7160C661 + sample 9: + time = 233000 + flags = 0 + data = length 620, hash 7A7AE07C + sample 10: + time = 300000 + flags = 0 + data = length 405, hash 5CC7F4E7 + sample 11: + time = 433000 + flags = 0 + data = length 4852, hash 9DB6979D + sample 12: + time = 400000 + flags = 0 + data = length 547, hash E31A6979 + sample 13: + time = 367000 + flags = 0 + data = length 570, hash FEC40D00 + sample 14: + time = 567000 + flags = 0 + data = length 5525, hash 7C478F7E + sample 15: + time = 500000 + flags = 0 + data = length 1082, hash DA07059A + sample 16: + time = 467000 + flags = 0 + data = length 807, hash 93478E6B + sample 17: + time = 533000 + flags = 0 + data = length 744, hash 9A8E6026 + sample 18: + time = 700000 + flags = 0 + data = length 4732, hash C73B23C0 + sample 19: + time = 633000 + flags = 0 + data = length 1004, hash 8A19A228 + sample 20: + time = 600000 + flags = 0 + data = length 794, hash 8126022C + sample 21: + time = 667000 + flags = 0 + data = length 645, hash F08300E5 + sample 22: + time = 833000 + flags = 0 + data = length 2684, hash 727FE378 + sample 23: + time = 767000 + flags = 0 + data = length 787, hash 419A7821 + sample 24: + time = 733000 + flags = 0 + data = length 649, hash 5C159346 + sample 25: + time = 800000 + flags = 0 + data = length 509, hash F912D655 + sample 26: + time = 967000 + flags = 0 + data = length 1226, hash 29815C21 + sample 27: + time = 900000 + flags = 0 + data = length 898, hash D997AD0A + sample 28: + time = 867000 + flags = 0 + data = length 476, hash A0423645 + sample 29: + time = 933000 + flags = 0 + data = length 486, hash DDF32CBB +track 2: + total output bytes = 12120 + sample count = 29 + format 0: + id = 2 + sampleMimeType = audio/ac3 + channelCount = 1 + sampleRate = 44100 + selectionFlags = 1 + language = und + sample 0: + time = 62000 + flags = 1 + data = length 416, hash 211F2286 + sample 1: + time = 97000 + flags = 1 + data = length 418, hash 77425A86 + sample 2: + time = 131000 + flags = 1 + data = length 418, hash A0FE5CA1 + sample 3: + time = 166000 + flags = 1 + data = length 418, hash 2309B066 + sample 4: + time = 201000 + flags = 1 + data = length 418, hash 928A653B + sample 5: + time = 236000 + flags = 1 + data = length 418, hash 3422F0CB + sample 6: + time = 270000 + flags = 1 + data = length 418, hash EFF43D5B + sample 7: + time = 306000 + flags = 1 + data = length 418, hash FC8093C7 + sample 8: + time = 341000 + flags = 1 + data = length 418, hash CCC08A16 + sample 9: + time = 376000 + flags = 1 + data = length 418, hash 2A6EE863 + sample 10: + time = 410000 + flags = 1 + data = length 418, hash D69A9251 + sample 11: + time = 445000 + flags = 1 + data = length 418, hash BCFB758D + sample 12: + time = 480000 + flags = 1 + data = length 418, hash 11B66799 + sample 13: + time = 514000 + flags = 1 + data = length 418, hash C824D392 + sample 14: + time = 550000 + flags = 1 + data = length 418, hash C167D872 + sample 15: + time = 585000 + flags = 1 + data = length 418, hash 4221C855 + sample 16: + time = 620000 + flags = 1 + data = length 418, hash 4D4FF934 + sample 17: + time = 654000 + flags = 1 + data = length 418, hash 984AA025 + sample 18: + time = 690000 + flags = 1 + data = length 418, hash BB788B46 + sample 19: + time = 724000 + flags = 1 + data = length 418, hash 9EFBFD97 + sample 20: + time = 759000 + flags = 1 + data = length 418, hash DF1A460C + sample 21: + time = 793000 + flags = 1 + data = length 418, hash 2BDB56A + sample 22: + time = 829000 + flags = 1 + data = length 418, hash CA230060 + sample 23: + time = 864000 + flags = 1 + data = length 418, hash D2F19F41 + sample 24: + time = 898000 + flags = 1 + data = length 418, hash AF392D79 + sample 25: + time = 932000 + flags = 1 + data = length 418, hash C5D7F2A3 + sample 26: + time = 968000 + flags = 1 + data = length 418, hash 733A35AE + sample 27: + time = 1002000 + flags = 1 + data = length 418, hash DE46E5D3 + sample 28: + time = 1037000 + flags = 1 + data = length 418, hash 56AB8D37 +track 3: + total output bytes = 49 + sample count = 1 + format 0: + id = 3 + sampleMimeType = application/x-subrip + selectionFlags = 1 + language = en + label = Subs Label + sample 0: + time = 0 + flags = 1 + data = length 49, hash DE7F89EF +tracksEnded = true diff --git a/testdata/src/test/assets/extractordumps/mkv/sample_with_null_terminated_ssa_subtitles.mkv.0.dump b/testdata/src/test/assets/extractordumps/mkv/sample_with_null_terminated_ssa_subtitles.mkv.0.dump new file mode 100644 index 0000000000..ec1eaf729b --- /dev/null +++ b/testdata/src/test/assets/extractordumps/mkv/sample_with_null_terminated_ssa_subtitles.mkv.0.dump @@ -0,0 +1,283 @@ +seekMap: + isSeekable = true + duration = 1139000 + getPosition(0) = [[timeUs=0, position=6106]] + getPosition(1) = [[timeUs=0, position=6106], [timeUs=67000, position=6106]] + getPosition(569500) = [[timeUs=67000, position=6106]] + getPosition(1139000) = [[timeUs=67000, position=6106]] +numberOfTracks = 3 +track 1: + total output bytes = 89502 + sample count = 30 + format 0: + id = 1 + sampleMimeType = video/avc + width = 1080 + height = 720 + selectionFlags = 1 + language = und + initializationData: + data = length 30, hash F6F3D010 + data = length 10, hash 7A0D0F2B + sample 0: + time = 67000 + flags = 1 + data = length 36477, hash F0F36CFE + sample 1: + time = 134000 + flags = 0 + data = length 5341, hash 40B85E2 + sample 2: + time = 100000 + flags = 0 + data = length 596, hash 357B4D92 + sample 3: + time = 267000 + flags = 0 + data = length 7704, hash A39EDA06 + sample 4: + time = 200000 + flags = 0 + data = length 989, hash 2813C72D + sample 5: + time = 167000 + flags = 0 + data = length 721, hash C50D1C73 + sample 6: + time = 234000 + flags = 0 + data = length 519, hash 65FE1911 + sample 7: + time = 400000 + flags = 0 + data = length 6160, hash E1CAC0EC + sample 8: + time = 334000 + flags = 0 + data = length 953, hash 7160C661 + sample 9: + time = 300000 + flags = 0 + data = length 620, hash 7A7AE07C + sample 10: + time = 367000 + flags = 0 + data = length 405, hash 5CC7F4E7 + sample 11: + time = 500000 + flags = 0 + data = length 4852, hash 9DB6979D + sample 12: + time = 467000 + flags = 0 + data = length 547, hash E31A6979 + sample 13: + time = 434000 + flags = 0 + data = length 570, hash FEC40D00 + sample 14: + time = 634000 + flags = 0 + data = length 5525, hash 7C478F7E + sample 15: + time = 567000 + flags = 0 + data = length 1082, hash DA07059A + sample 16: + time = 534000 + flags = 0 + data = length 807, hash 93478E6B + sample 17: + time = 600000 + flags = 0 + data = length 744, hash 9A8E6026 + sample 18: + time = 767000 + flags = 0 + data = length 4732, hash C73B23C0 + sample 19: + time = 700000 + flags = 0 + data = length 1004, hash 8A19A228 + sample 20: + time = 667000 + flags = 0 + data = length 794, hash 8126022C + sample 21: + time = 734000 + flags = 0 + data = length 645, hash F08300E5 + sample 22: + time = 900000 + flags = 0 + data = length 2684, hash 727FE378 + sample 23: + time = 834000 + flags = 0 + data = length 787, hash 419A7821 + sample 24: + time = 800000 + flags = 0 + data = length 649, hash 5C159346 + sample 25: + time = 867000 + flags = 0 + data = length 509, hash F912D655 + sample 26: + time = 1034000 + flags = 0 + data = length 1226, hash 29815C21 + sample 27: + time = 967000 + flags = 0 + data = length 898, hash D997AD0A + sample 28: + time = 934000 + flags = 0 + data = length 476, hash A0423645 + sample 29: + time = 1000000 + flags = 0 + data = length 486, hash DDF32CBB +track 2: + total output bytes = 12120 + sample count = 29 + format 0: + id = 2 + sampleMimeType = audio/ac3 + channelCount = 1 + sampleRate = 44100 + selectionFlags = 1 + language = und + sample 0: + time = 129000 + flags = 1 + data = length 416, hash 211F2286 + sample 1: + time = 163829 + flags = 1 + data = length 418, hash 77425A86 + sample 2: + time = 198659 + flags = 1 + data = length 418, hash A0FE5CA1 + sample 3: + time = 233489 + flags = 1 + data = length 418, hash 2309B066 + sample 4: + time = 268319 + flags = 1 + data = length 418, hash 928A653B + sample 5: + time = 303149 + flags = 1 + data = length 418, hash 3422F0CB + sample 6: + time = 337979 + flags = 1 + data = length 418, hash EFF43D5B + sample 7: + time = 372809 + flags = 1 + data = length 418, hash FC8093C7 + sample 8: + time = 408000 + flags = 1 + data = length 418, hash CCC08A16 + sample 9: + time = 442829 + flags = 1 + data = length 418, hash 2A6EE863 + sample 10: + time = 477659 + flags = 1 + data = length 418, hash D69A9251 + sample 11: + time = 512489 + flags = 1 + data = length 418, hash BCFB758D + sample 12: + time = 547319 + flags = 1 + data = length 418, hash 11B66799 + sample 13: + time = 582149 + flags = 1 + data = length 418, hash C824D392 + sample 14: + time = 616979 + flags = 1 + data = length 418, hash C167D872 + sample 15: + time = 651809 + flags = 1 + data = length 418, hash 4221C855 + sample 16: + time = 687000 + flags = 1 + data = length 418, hash 4D4FF934 + sample 17: + time = 721829 + flags = 1 + data = length 418, hash 984AA025 + sample 18: + time = 756659 + flags = 1 + data = length 418, hash BB788B46 + sample 19: + time = 791489 + flags = 1 + data = length 418, hash 9EFBFD97 + sample 20: + time = 826319 + flags = 1 + data = length 418, hash DF1A460C + sample 21: + time = 861149 + flags = 1 + data = length 418, hash 2BDB56A + sample 22: + time = 895979 + flags = 1 + data = length 418, hash CA230060 + sample 23: + time = 930809 + flags = 1 + data = length 418, hash D2F19F41 + sample 24: + time = 965000 + flags = 1 + data = length 418, hash AF392D79 + sample 25: + time = 999829 + flags = 1 + data = length 418, hash C5D7F2A3 + sample 26: + time = 1034659 + flags = 1 + data = length 418, hash 733A35AE + sample 27: + time = 1069489 + flags = 1 + data = length 418, hash DE46E5D3 + sample 28: + time = 1104319 + flags = 1 + data = length 418, hash 56AB8D37 +track 3: + total output bytes = 71 + sample count = 1 + format 0: + id = 3 + sampleMimeType = text/x-ssa + selectionFlags = 1 + language = und + initializationData: + data = length 90, hash A5E21974 + data = length 470, hash 40E7D996 + sample 0: + time = 0 + flags = 1 + data = length 71, hash 2B8E631C +tracksEnded = true diff --git a/testdata/src/test/assets/extractordumps/mkv/sample_with_null_terminated_ssa_subtitles.mkv.1.dump b/testdata/src/test/assets/extractordumps/mkv/sample_with_null_terminated_ssa_subtitles.mkv.1.dump new file mode 100644 index 0000000000..ec1eaf729b --- /dev/null +++ b/testdata/src/test/assets/extractordumps/mkv/sample_with_null_terminated_ssa_subtitles.mkv.1.dump @@ -0,0 +1,283 @@ +seekMap: + isSeekable = true + duration = 1139000 + getPosition(0) = [[timeUs=0, position=6106]] + getPosition(1) = [[timeUs=0, position=6106], [timeUs=67000, position=6106]] + getPosition(569500) = [[timeUs=67000, position=6106]] + getPosition(1139000) = [[timeUs=67000, position=6106]] +numberOfTracks = 3 +track 1: + total output bytes = 89502 + sample count = 30 + format 0: + id = 1 + sampleMimeType = video/avc + width = 1080 + height = 720 + selectionFlags = 1 + language = und + initializationData: + data = length 30, hash F6F3D010 + data = length 10, hash 7A0D0F2B + sample 0: + time = 67000 + flags = 1 + data = length 36477, hash F0F36CFE + sample 1: + time = 134000 + flags = 0 + data = length 5341, hash 40B85E2 + sample 2: + time = 100000 + flags = 0 + data = length 596, hash 357B4D92 + sample 3: + time = 267000 + flags = 0 + data = length 7704, hash A39EDA06 + sample 4: + time = 200000 + flags = 0 + data = length 989, hash 2813C72D + sample 5: + time = 167000 + flags = 0 + data = length 721, hash C50D1C73 + sample 6: + time = 234000 + flags = 0 + data = length 519, hash 65FE1911 + sample 7: + time = 400000 + flags = 0 + data = length 6160, hash E1CAC0EC + sample 8: + time = 334000 + flags = 0 + data = length 953, hash 7160C661 + sample 9: + time = 300000 + flags = 0 + data = length 620, hash 7A7AE07C + sample 10: + time = 367000 + flags = 0 + data = length 405, hash 5CC7F4E7 + sample 11: + time = 500000 + flags = 0 + data = length 4852, hash 9DB6979D + sample 12: + time = 467000 + flags = 0 + data = length 547, hash E31A6979 + sample 13: + time = 434000 + flags = 0 + data = length 570, hash FEC40D00 + sample 14: + time = 634000 + flags = 0 + data = length 5525, hash 7C478F7E + sample 15: + time = 567000 + flags = 0 + data = length 1082, hash DA07059A + sample 16: + time = 534000 + flags = 0 + data = length 807, hash 93478E6B + sample 17: + time = 600000 + flags = 0 + data = length 744, hash 9A8E6026 + sample 18: + time = 767000 + flags = 0 + data = length 4732, hash C73B23C0 + sample 19: + time = 700000 + flags = 0 + data = length 1004, hash 8A19A228 + sample 20: + time = 667000 + flags = 0 + data = length 794, hash 8126022C + sample 21: + time = 734000 + flags = 0 + data = length 645, hash F08300E5 + sample 22: + time = 900000 + flags = 0 + data = length 2684, hash 727FE378 + sample 23: + time = 834000 + flags = 0 + data = length 787, hash 419A7821 + sample 24: + time = 800000 + flags = 0 + data = length 649, hash 5C159346 + sample 25: + time = 867000 + flags = 0 + data = length 509, hash F912D655 + sample 26: + time = 1034000 + flags = 0 + data = length 1226, hash 29815C21 + sample 27: + time = 967000 + flags = 0 + data = length 898, hash D997AD0A + sample 28: + time = 934000 + flags = 0 + data = length 476, hash A0423645 + sample 29: + time = 1000000 + flags = 0 + data = length 486, hash DDF32CBB +track 2: + total output bytes = 12120 + sample count = 29 + format 0: + id = 2 + sampleMimeType = audio/ac3 + channelCount = 1 + sampleRate = 44100 + selectionFlags = 1 + language = und + sample 0: + time = 129000 + flags = 1 + data = length 416, hash 211F2286 + sample 1: + time = 163829 + flags = 1 + data = length 418, hash 77425A86 + sample 2: + time = 198659 + flags = 1 + data = length 418, hash A0FE5CA1 + sample 3: + time = 233489 + flags = 1 + data = length 418, hash 2309B066 + sample 4: + time = 268319 + flags = 1 + data = length 418, hash 928A653B + sample 5: + time = 303149 + flags = 1 + data = length 418, hash 3422F0CB + sample 6: + time = 337979 + flags = 1 + data = length 418, hash EFF43D5B + sample 7: + time = 372809 + flags = 1 + data = length 418, hash FC8093C7 + sample 8: + time = 408000 + flags = 1 + data = length 418, hash CCC08A16 + sample 9: + time = 442829 + flags = 1 + data = length 418, hash 2A6EE863 + sample 10: + time = 477659 + flags = 1 + data = length 418, hash D69A9251 + sample 11: + time = 512489 + flags = 1 + data = length 418, hash BCFB758D + sample 12: + time = 547319 + flags = 1 + data = length 418, hash 11B66799 + sample 13: + time = 582149 + flags = 1 + data = length 418, hash C824D392 + sample 14: + time = 616979 + flags = 1 + data = length 418, hash C167D872 + sample 15: + time = 651809 + flags = 1 + data = length 418, hash 4221C855 + sample 16: + time = 687000 + flags = 1 + data = length 418, hash 4D4FF934 + sample 17: + time = 721829 + flags = 1 + data = length 418, hash 984AA025 + sample 18: + time = 756659 + flags = 1 + data = length 418, hash BB788B46 + sample 19: + time = 791489 + flags = 1 + data = length 418, hash 9EFBFD97 + sample 20: + time = 826319 + flags = 1 + data = length 418, hash DF1A460C + sample 21: + time = 861149 + flags = 1 + data = length 418, hash 2BDB56A + sample 22: + time = 895979 + flags = 1 + data = length 418, hash CA230060 + sample 23: + time = 930809 + flags = 1 + data = length 418, hash D2F19F41 + sample 24: + time = 965000 + flags = 1 + data = length 418, hash AF392D79 + sample 25: + time = 999829 + flags = 1 + data = length 418, hash C5D7F2A3 + sample 26: + time = 1034659 + flags = 1 + data = length 418, hash 733A35AE + sample 27: + time = 1069489 + flags = 1 + data = length 418, hash DE46E5D3 + sample 28: + time = 1104319 + flags = 1 + data = length 418, hash 56AB8D37 +track 3: + total output bytes = 71 + sample count = 1 + format 0: + id = 3 + sampleMimeType = text/x-ssa + selectionFlags = 1 + language = und + initializationData: + data = length 90, hash A5E21974 + data = length 470, hash 40E7D996 + sample 0: + time = 0 + flags = 1 + data = length 71, hash 2B8E631C +tracksEnded = true diff --git a/testdata/src/test/assets/extractordumps/mkv/sample_with_null_terminated_ssa_subtitles.mkv.2.dump b/testdata/src/test/assets/extractordumps/mkv/sample_with_null_terminated_ssa_subtitles.mkv.2.dump new file mode 100644 index 0000000000..ec1eaf729b --- /dev/null +++ b/testdata/src/test/assets/extractordumps/mkv/sample_with_null_terminated_ssa_subtitles.mkv.2.dump @@ -0,0 +1,283 @@ +seekMap: + isSeekable = true + duration = 1139000 + getPosition(0) = [[timeUs=0, position=6106]] + getPosition(1) = [[timeUs=0, position=6106], [timeUs=67000, position=6106]] + getPosition(569500) = [[timeUs=67000, position=6106]] + getPosition(1139000) = [[timeUs=67000, position=6106]] +numberOfTracks = 3 +track 1: + total output bytes = 89502 + sample count = 30 + format 0: + id = 1 + sampleMimeType = video/avc + width = 1080 + height = 720 + selectionFlags = 1 + language = und + initializationData: + data = length 30, hash F6F3D010 + data = length 10, hash 7A0D0F2B + sample 0: + time = 67000 + flags = 1 + data = length 36477, hash F0F36CFE + sample 1: + time = 134000 + flags = 0 + data = length 5341, hash 40B85E2 + sample 2: + time = 100000 + flags = 0 + data = length 596, hash 357B4D92 + sample 3: + time = 267000 + flags = 0 + data = length 7704, hash A39EDA06 + sample 4: + time = 200000 + flags = 0 + data = length 989, hash 2813C72D + sample 5: + time = 167000 + flags = 0 + data = length 721, hash C50D1C73 + sample 6: + time = 234000 + flags = 0 + data = length 519, hash 65FE1911 + sample 7: + time = 400000 + flags = 0 + data = length 6160, hash E1CAC0EC + sample 8: + time = 334000 + flags = 0 + data = length 953, hash 7160C661 + sample 9: + time = 300000 + flags = 0 + data = length 620, hash 7A7AE07C + sample 10: + time = 367000 + flags = 0 + data = length 405, hash 5CC7F4E7 + sample 11: + time = 500000 + flags = 0 + data = length 4852, hash 9DB6979D + sample 12: + time = 467000 + flags = 0 + data = length 547, hash E31A6979 + sample 13: + time = 434000 + flags = 0 + data = length 570, hash FEC40D00 + sample 14: + time = 634000 + flags = 0 + data = length 5525, hash 7C478F7E + sample 15: + time = 567000 + flags = 0 + data = length 1082, hash DA07059A + sample 16: + time = 534000 + flags = 0 + data = length 807, hash 93478E6B + sample 17: + time = 600000 + flags = 0 + data = length 744, hash 9A8E6026 + sample 18: + time = 767000 + flags = 0 + data = length 4732, hash C73B23C0 + sample 19: + time = 700000 + flags = 0 + data = length 1004, hash 8A19A228 + sample 20: + time = 667000 + flags = 0 + data = length 794, hash 8126022C + sample 21: + time = 734000 + flags = 0 + data = length 645, hash F08300E5 + sample 22: + time = 900000 + flags = 0 + data = length 2684, hash 727FE378 + sample 23: + time = 834000 + flags = 0 + data = length 787, hash 419A7821 + sample 24: + time = 800000 + flags = 0 + data = length 649, hash 5C159346 + sample 25: + time = 867000 + flags = 0 + data = length 509, hash F912D655 + sample 26: + time = 1034000 + flags = 0 + data = length 1226, hash 29815C21 + sample 27: + time = 967000 + flags = 0 + data = length 898, hash D997AD0A + sample 28: + time = 934000 + flags = 0 + data = length 476, hash A0423645 + sample 29: + time = 1000000 + flags = 0 + data = length 486, hash DDF32CBB +track 2: + total output bytes = 12120 + sample count = 29 + format 0: + id = 2 + sampleMimeType = audio/ac3 + channelCount = 1 + sampleRate = 44100 + selectionFlags = 1 + language = und + sample 0: + time = 129000 + flags = 1 + data = length 416, hash 211F2286 + sample 1: + time = 163829 + flags = 1 + data = length 418, hash 77425A86 + sample 2: + time = 198659 + flags = 1 + data = length 418, hash A0FE5CA1 + sample 3: + time = 233489 + flags = 1 + data = length 418, hash 2309B066 + sample 4: + time = 268319 + flags = 1 + data = length 418, hash 928A653B + sample 5: + time = 303149 + flags = 1 + data = length 418, hash 3422F0CB + sample 6: + time = 337979 + flags = 1 + data = length 418, hash EFF43D5B + sample 7: + time = 372809 + flags = 1 + data = length 418, hash FC8093C7 + sample 8: + time = 408000 + flags = 1 + data = length 418, hash CCC08A16 + sample 9: + time = 442829 + flags = 1 + data = length 418, hash 2A6EE863 + sample 10: + time = 477659 + flags = 1 + data = length 418, hash D69A9251 + sample 11: + time = 512489 + flags = 1 + data = length 418, hash BCFB758D + sample 12: + time = 547319 + flags = 1 + data = length 418, hash 11B66799 + sample 13: + time = 582149 + flags = 1 + data = length 418, hash C824D392 + sample 14: + time = 616979 + flags = 1 + data = length 418, hash C167D872 + sample 15: + time = 651809 + flags = 1 + data = length 418, hash 4221C855 + sample 16: + time = 687000 + flags = 1 + data = length 418, hash 4D4FF934 + sample 17: + time = 721829 + flags = 1 + data = length 418, hash 984AA025 + sample 18: + time = 756659 + flags = 1 + data = length 418, hash BB788B46 + sample 19: + time = 791489 + flags = 1 + data = length 418, hash 9EFBFD97 + sample 20: + time = 826319 + flags = 1 + data = length 418, hash DF1A460C + sample 21: + time = 861149 + flags = 1 + data = length 418, hash 2BDB56A + sample 22: + time = 895979 + flags = 1 + data = length 418, hash CA230060 + sample 23: + time = 930809 + flags = 1 + data = length 418, hash D2F19F41 + sample 24: + time = 965000 + flags = 1 + data = length 418, hash AF392D79 + sample 25: + time = 999829 + flags = 1 + data = length 418, hash C5D7F2A3 + sample 26: + time = 1034659 + flags = 1 + data = length 418, hash 733A35AE + sample 27: + time = 1069489 + flags = 1 + data = length 418, hash DE46E5D3 + sample 28: + time = 1104319 + flags = 1 + data = length 418, hash 56AB8D37 +track 3: + total output bytes = 71 + sample count = 1 + format 0: + id = 3 + sampleMimeType = text/x-ssa + selectionFlags = 1 + language = und + initializationData: + data = length 90, hash A5E21974 + data = length 470, hash 40E7D996 + sample 0: + time = 0 + flags = 1 + data = length 71, hash 2B8E631C +tracksEnded = true diff --git a/testdata/src/test/assets/extractordumps/mkv/sample_with_null_terminated_ssa_subtitles.mkv.3.dump b/testdata/src/test/assets/extractordumps/mkv/sample_with_null_terminated_ssa_subtitles.mkv.3.dump new file mode 100644 index 0000000000..ec1eaf729b --- /dev/null +++ b/testdata/src/test/assets/extractordumps/mkv/sample_with_null_terminated_ssa_subtitles.mkv.3.dump @@ -0,0 +1,283 @@ +seekMap: + isSeekable = true + duration = 1139000 + getPosition(0) = [[timeUs=0, position=6106]] + getPosition(1) = [[timeUs=0, position=6106], [timeUs=67000, position=6106]] + getPosition(569500) = [[timeUs=67000, position=6106]] + getPosition(1139000) = [[timeUs=67000, position=6106]] +numberOfTracks = 3 +track 1: + total output bytes = 89502 + sample count = 30 + format 0: + id = 1 + sampleMimeType = video/avc + width = 1080 + height = 720 + selectionFlags = 1 + language = und + initializationData: + data = length 30, hash F6F3D010 + data = length 10, hash 7A0D0F2B + sample 0: + time = 67000 + flags = 1 + data = length 36477, hash F0F36CFE + sample 1: + time = 134000 + flags = 0 + data = length 5341, hash 40B85E2 + sample 2: + time = 100000 + flags = 0 + data = length 596, hash 357B4D92 + sample 3: + time = 267000 + flags = 0 + data = length 7704, hash A39EDA06 + sample 4: + time = 200000 + flags = 0 + data = length 989, hash 2813C72D + sample 5: + time = 167000 + flags = 0 + data = length 721, hash C50D1C73 + sample 6: + time = 234000 + flags = 0 + data = length 519, hash 65FE1911 + sample 7: + time = 400000 + flags = 0 + data = length 6160, hash E1CAC0EC + sample 8: + time = 334000 + flags = 0 + data = length 953, hash 7160C661 + sample 9: + time = 300000 + flags = 0 + data = length 620, hash 7A7AE07C + sample 10: + time = 367000 + flags = 0 + data = length 405, hash 5CC7F4E7 + sample 11: + time = 500000 + flags = 0 + data = length 4852, hash 9DB6979D + sample 12: + time = 467000 + flags = 0 + data = length 547, hash E31A6979 + sample 13: + time = 434000 + flags = 0 + data = length 570, hash FEC40D00 + sample 14: + time = 634000 + flags = 0 + data = length 5525, hash 7C478F7E + sample 15: + time = 567000 + flags = 0 + data = length 1082, hash DA07059A + sample 16: + time = 534000 + flags = 0 + data = length 807, hash 93478E6B + sample 17: + time = 600000 + flags = 0 + data = length 744, hash 9A8E6026 + sample 18: + time = 767000 + flags = 0 + data = length 4732, hash C73B23C0 + sample 19: + time = 700000 + flags = 0 + data = length 1004, hash 8A19A228 + sample 20: + time = 667000 + flags = 0 + data = length 794, hash 8126022C + sample 21: + time = 734000 + flags = 0 + data = length 645, hash F08300E5 + sample 22: + time = 900000 + flags = 0 + data = length 2684, hash 727FE378 + sample 23: + time = 834000 + flags = 0 + data = length 787, hash 419A7821 + sample 24: + time = 800000 + flags = 0 + data = length 649, hash 5C159346 + sample 25: + time = 867000 + flags = 0 + data = length 509, hash F912D655 + sample 26: + time = 1034000 + flags = 0 + data = length 1226, hash 29815C21 + sample 27: + time = 967000 + flags = 0 + data = length 898, hash D997AD0A + sample 28: + time = 934000 + flags = 0 + data = length 476, hash A0423645 + sample 29: + time = 1000000 + flags = 0 + data = length 486, hash DDF32CBB +track 2: + total output bytes = 12120 + sample count = 29 + format 0: + id = 2 + sampleMimeType = audio/ac3 + channelCount = 1 + sampleRate = 44100 + selectionFlags = 1 + language = und + sample 0: + time = 129000 + flags = 1 + data = length 416, hash 211F2286 + sample 1: + time = 163829 + flags = 1 + data = length 418, hash 77425A86 + sample 2: + time = 198659 + flags = 1 + data = length 418, hash A0FE5CA1 + sample 3: + time = 233489 + flags = 1 + data = length 418, hash 2309B066 + sample 4: + time = 268319 + flags = 1 + data = length 418, hash 928A653B + sample 5: + time = 303149 + flags = 1 + data = length 418, hash 3422F0CB + sample 6: + time = 337979 + flags = 1 + data = length 418, hash EFF43D5B + sample 7: + time = 372809 + flags = 1 + data = length 418, hash FC8093C7 + sample 8: + time = 408000 + flags = 1 + data = length 418, hash CCC08A16 + sample 9: + time = 442829 + flags = 1 + data = length 418, hash 2A6EE863 + sample 10: + time = 477659 + flags = 1 + data = length 418, hash D69A9251 + sample 11: + time = 512489 + flags = 1 + data = length 418, hash BCFB758D + sample 12: + time = 547319 + flags = 1 + data = length 418, hash 11B66799 + sample 13: + time = 582149 + flags = 1 + data = length 418, hash C824D392 + sample 14: + time = 616979 + flags = 1 + data = length 418, hash C167D872 + sample 15: + time = 651809 + flags = 1 + data = length 418, hash 4221C855 + sample 16: + time = 687000 + flags = 1 + data = length 418, hash 4D4FF934 + sample 17: + time = 721829 + flags = 1 + data = length 418, hash 984AA025 + sample 18: + time = 756659 + flags = 1 + data = length 418, hash BB788B46 + sample 19: + time = 791489 + flags = 1 + data = length 418, hash 9EFBFD97 + sample 20: + time = 826319 + flags = 1 + data = length 418, hash DF1A460C + sample 21: + time = 861149 + flags = 1 + data = length 418, hash 2BDB56A + sample 22: + time = 895979 + flags = 1 + data = length 418, hash CA230060 + sample 23: + time = 930809 + flags = 1 + data = length 418, hash D2F19F41 + sample 24: + time = 965000 + flags = 1 + data = length 418, hash AF392D79 + sample 25: + time = 999829 + flags = 1 + data = length 418, hash C5D7F2A3 + sample 26: + time = 1034659 + flags = 1 + data = length 418, hash 733A35AE + sample 27: + time = 1069489 + flags = 1 + data = length 418, hash DE46E5D3 + sample 28: + time = 1104319 + flags = 1 + data = length 418, hash 56AB8D37 +track 3: + total output bytes = 71 + sample count = 1 + format 0: + id = 3 + sampleMimeType = text/x-ssa + selectionFlags = 1 + language = und + initializationData: + data = length 90, hash A5E21974 + data = length 470, hash 40E7D996 + sample 0: + time = 0 + flags = 1 + data = length 71, hash 2B8E631C +tracksEnded = true diff --git a/testdata/src/test/assets/extractordumps/mkv/sample_with_null_terminated_ssa_subtitles.mkv.unknown_length.dump b/testdata/src/test/assets/extractordumps/mkv/sample_with_null_terminated_ssa_subtitles.mkv.unknown_length.dump new file mode 100644 index 0000000000..ec1eaf729b --- /dev/null +++ b/testdata/src/test/assets/extractordumps/mkv/sample_with_null_terminated_ssa_subtitles.mkv.unknown_length.dump @@ -0,0 +1,283 @@ +seekMap: + isSeekable = true + duration = 1139000 + getPosition(0) = [[timeUs=0, position=6106]] + getPosition(1) = [[timeUs=0, position=6106], [timeUs=67000, position=6106]] + getPosition(569500) = [[timeUs=67000, position=6106]] + getPosition(1139000) = [[timeUs=67000, position=6106]] +numberOfTracks = 3 +track 1: + total output bytes = 89502 + sample count = 30 + format 0: + id = 1 + sampleMimeType = video/avc + width = 1080 + height = 720 + selectionFlags = 1 + language = und + initializationData: + data = length 30, hash F6F3D010 + data = length 10, hash 7A0D0F2B + sample 0: + time = 67000 + flags = 1 + data = length 36477, hash F0F36CFE + sample 1: + time = 134000 + flags = 0 + data = length 5341, hash 40B85E2 + sample 2: + time = 100000 + flags = 0 + data = length 596, hash 357B4D92 + sample 3: + time = 267000 + flags = 0 + data = length 7704, hash A39EDA06 + sample 4: + time = 200000 + flags = 0 + data = length 989, hash 2813C72D + sample 5: + time = 167000 + flags = 0 + data = length 721, hash C50D1C73 + sample 6: + time = 234000 + flags = 0 + data = length 519, hash 65FE1911 + sample 7: + time = 400000 + flags = 0 + data = length 6160, hash E1CAC0EC + sample 8: + time = 334000 + flags = 0 + data = length 953, hash 7160C661 + sample 9: + time = 300000 + flags = 0 + data = length 620, hash 7A7AE07C + sample 10: + time = 367000 + flags = 0 + data = length 405, hash 5CC7F4E7 + sample 11: + time = 500000 + flags = 0 + data = length 4852, hash 9DB6979D + sample 12: + time = 467000 + flags = 0 + data = length 547, hash E31A6979 + sample 13: + time = 434000 + flags = 0 + data = length 570, hash FEC40D00 + sample 14: + time = 634000 + flags = 0 + data = length 5525, hash 7C478F7E + sample 15: + time = 567000 + flags = 0 + data = length 1082, hash DA07059A + sample 16: + time = 534000 + flags = 0 + data = length 807, hash 93478E6B + sample 17: + time = 600000 + flags = 0 + data = length 744, hash 9A8E6026 + sample 18: + time = 767000 + flags = 0 + data = length 4732, hash C73B23C0 + sample 19: + time = 700000 + flags = 0 + data = length 1004, hash 8A19A228 + sample 20: + time = 667000 + flags = 0 + data = length 794, hash 8126022C + sample 21: + time = 734000 + flags = 0 + data = length 645, hash F08300E5 + sample 22: + time = 900000 + flags = 0 + data = length 2684, hash 727FE378 + sample 23: + time = 834000 + flags = 0 + data = length 787, hash 419A7821 + sample 24: + time = 800000 + flags = 0 + data = length 649, hash 5C159346 + sample 25: + time = 867000 + flags = 0 + data = length 509, hash F912D655 + sample 26: + time = 1034000 + flags = 0 + data = length 1226, hash 29815C21 + sample 27: + time = 967000 + flags = 0 + data = length 898, hash D997AD0A + sample 28: + time = 934000 + flags = 0 + data = length 476, hash A0423645 + sample 29: + time = 1000000 + flags = 0 + data = length 486, hash DDF32CBB +track 2: + total output bytes = 12120 + sample count = 29 + format 0: + id = 2 + sampleMimeType = audio/ac3 + channelCount = 1 + sampleRate = 44100 + selectionFlags = 1 + language = und + sample 0: + time = 129000 + flags = 1 + data = length 416, hash 211F2286 + sample 1: + time = 163829 + flags = 1 + data = length 418, hash 77425A86 + sample 2: + time = 198659 + flags = 1 + data = length 418, hash A0FE5CA1 + sample 3: + time = 233489 + flags = 1 + data = length 418, hash 2309B066 + sample 4: + time = 268319 + flags = 1 + data = length 418, hash 928A653B + sample 5: + time = 303149 + flags = 1 + data = length 418, hash 3422F0CB + sample 6: + time = 337979 + flags = 1 + data = length 418, hash EFF43D5B + sample 7: + time = 372809 + flags = 1 + data = length 418, hash FC8093C7 + sample 8: + time = 408000 + flags = 1 + data = length 418, hash CCC08A16 + sample 9: + time = 442829 + flags = 1 + data = length 418, hash 2A6EE863 + sample 10: + time = 477659 + flags = 1 + data = length 418, hash D69A9251 + sample 11: + time = 512489 + flags = 1 + data = length 418, hash BCFB758D + sample 12: + time = 547319 + flags = 1 + data = length 418, hash 11B66799 + sample 13: + time = 582149 + flags = 1 + data = length 418, hash C824D392 + sample 14: + time = 616979 + flags = 1 + data = length 418, hash C167D872 + sample 15: + time = 651809 + flags = 1 + data = length 418, hash 4221C855 + sample 16: + time = 687000 + flags = 1 + data = length 418, hash 4D4FF934 + sample 17: + time = 721829 + flags = 1 + data = length 418, hash 984AA025 + sample 18: + time = 756659 + flags = 1 + data = length 418, hash BB788B46 + sample 19: + time = 791489 + flags = 1 + data = length 418, hash 9EFBFD97 + sample 20: + time = 826319 + flags = 1 + data = length 418, hash DF1A460C + sample 21: + time = 861149 + flags = 1 + data = length 418, hash 2BDB56A + sample 22: + time = 895979 + flags = 1 + data = length 418, hash CA230060 + sample 23: + time = 930809 + flags = 1 + data = length 418, hash D2F19F41 + sample 24: + time = 965000 + flags = 1 + data = length 418, hash AF392D79 + sample 25: + time = 999829 + flags = 1 + data = length 418, hash C5D7F2A3 + sample 26: + time = 1034659 + flags = 1 + data = length 418, hash 733A35AE + sample 27: + time = 1069489 + flags = 1 + data = length 418, hash DE46E5D3 + sample 28: + time = 1104319 + flags = 1 + data = length 418, hash 56AB8D37 +track 3: + total output bytes = 71 + sample count = 1 + format 0: + id = 3 + sampleMimeType = text/x-ssa + selectionFlags = 1 + language = und + initializationData: + data = length 90, hash A5E21974 + data = length 470, hash 40E7D996 + sample 0: + time = 0 + flags = 1 + data = length 71, hash 2B8E631C +tracksEnded = true diff --git a/testdata/src/test/assets/extractordumps/mkv/sample_with_ssa_subtitles.mkv.0.dump b/testdata/src/test/assets/extractordumps/mkv/sample_with_ssa_subtitles.mkv.0.dump new file mode 100644 index 0000000000..16df131ed3 --- /dev/null +++ b/testdata/src/test/assets/extractordumps/mkv/sample_with_ssa_subtitles.mkv.0.dump @@ -0,0 +1,283 @@ +seekMap: + isSeekable = true + duration = 1139000 + getPosition(0) = [[timeUs=0, position=6106]] + getPosition(1) = [[timeUs=0, position=6106], [timeUs=67000, position=6106]] + getPosition(569500) = [[timeUs=67000, position=6106]] + getPosition(1139000) = [[timeUs=67000, position=6106]] +numberOfTracks = 3 +track 1: + total output bytes = 89502 + sample count = 30 + format 0: + id = 1 + sampleMimeType = video/avc + width = 1080 + height = 720 + selectionFlags = 1 + language = und + initializationData: + data = length 30, hash F6F3D010 + data = length 10, hash 7A0D0F2B + sample 0: + time = 67000 + flags = 1 + data = length 36477, hash F0F36CFE + sample 1: + time = 134000 + flags = 0 + data = length 5341, hash 40B85E2 + sample 2: + time = 100000 + flags = 0 + data = length 596, hash 357B4D92 + sample 3: + time = 267000 + flags = 0 + data = length 7704, hash A39EDA06 + sample 4: + time = 200000 + flags = 0 + data = length 989, hash 2813C72D + sample 5: + time = 167000 + flags = 0 + data = length 721, hash C50D1C73 + sample 6: + time = 234000 + flags = 0 + data = length 519, hash 65FE1911 + sample 7: + time = 400000 + flags = 0 + data = length 6160, hash E1CAC0EC + sample 8: + time = 334000 + flags = 0 + data = length 953, hash 7160C661 + sample 9: + time = 300000 + flags = 0 + data = length 620, hash 7A7AE07C + sample 10: + time = 367000 + flags = 0 + data = length 405, hash 5CC7F4E7 + sample 11: + time = 500000 + flags = 0 + data = length 4852, hash 9DB6979D + sample 12: + time = 467000 + flags = 0 + data = length 547, hash E31A6979 + sample 13: + time = 434000 + flags = 0 + data = length 570, hash FEC40D00 + sample 14: + time = 634000 + flags = 0 + data = length 5525, hash 7C478F7E + sample 15: + time = 567000 + flags = 0 + data = length 1082, hash DA07059A + sample 16: + time = 534000 + flags = 0 + data = length 807, hash 93478E6B + sample 17: + time = 600000 + flags = 0 + data = length 744, hash 9A8E6026 + sample 18: + time = 767000 + flags = 0 + data = length 4732, hash C73B23C0 + sample 19: + time = 700000 + flags = 0 + data = length 1004, hash 8A19A228 + sample 20: + time = 667000 + flags = 0 + data = length 794, hash 8126022C + sample 21: + time = 734000 + flags = 0 + data = length 645, hash F08300E5 + sample 22: + time = 900000 + flags = 0 + data = length 2684, hash 727FE378 + sample 23: + time = 834000 + flags = 0 + data = length 787, hash 419A7821 + sample 24: + time = 800000 + flags = 0 + data = length 649, hash 5C159346 + sample 25: + time = 867000 + flags = 0 + data = length 509, hash F912D655 + sample 26: + time = 1034000 + flags = 0 + data = length 1226, hash 29815C21 + sample 27: + time = 967000 + flags = 0 + data = length 898, hash D997AD0A + sample 28: + time = 934000 + flags = 0 + data = length 476, hash A0423645 + sample 29: + time = 1000000 + flags = 0 + data = length 486, hash DDF32CBB +track 2: + total output bytes = 12120 + sample count = 29 + format 0: + id = 2 + sampleMimeType = audio/ac3 + channelCount = 1 + sampleRate = 44100 + selectionFlags = 1 + language = und + sample 0: + time = 129000 + flags = 1 + data = length 416, hash 211F2286 + sample 1: + time = 163829 + flags = 1 + data = length 418, hash 77425A86 + sample 2: + time = 198659 + flags = 1 + data = length 418, hash A0FE5CA1 + sample 3: + time = 233489 + flags = 1 + data = length 418, hash 2309B066 + sample 4: + time = 268319 + flags = 1 + data = length 418, hash 928A653B + sample 5: + time = 303149 + flags = 1 + data = length 418, hash 3422F0CB + sample 6: + time = 337979 + flags = 1 + data = length 418, hash EFF43D5B + sample 7: + time = 372809 + flags = 1 + data = length 418, hash FC8093C7 + sample 8: + time = 408000 + flags = 1 + data = length 418, hash CCC08A16 + sample 9: + time = 442829 + flags = 1 + data = length 418, hash 2A6EE863 + sample 10: + time = 477659 + flags = 1 + data = length 418, hash D69A9251 + sample 11: + time = 512489 + flags = 1 + data = length 418, hash BCFB758D + sample 12: + time = 547319 + flags = 1 + data = length 418, hash 11B66799 + sample 13: + time = 582149 + flags = 1 + data = length 418, hash C824D392 + sample 14: + time = 616979 + flags = 1 + data = length 418, hash C167D872 + sample 15: + time = 651809 + flags = 1 + data = length 418, hash 4221C855 + sample 16: + time = 687000 + flags = 1 + data = length 418, hash 4D4FF934 + sample 17: + time = 721829 + flags = 1 + data = length 418, hash 984AA025 + sample 18: + time = 756659 + flags = 1 + data = length 418, hash BB788B46 + sample 19: + time = 791489 + flags = 1 + data = length 418, hash 9EFBFD97 + sample 20: + time = 826319 + flags = 1 + data = length 418, hash DF1A460C + sample 21: + time = 861149 + flags = 1 + data = length 418, hash 2BDB56A + sample 22: + time = 895979 + flags = 1 + data = length 418, hash CA230060 + sample 23: + time = 930809 + flags = 1 + data = length 418, hash D2F19F41 + sample 24: + time = 965000 + flags = 1 + data = length 418, hash AF392D79 + sample 25: + time = 999829 + flags = 1 + data = length 418, hash C5D7F2A3 + sample 26: + time = 1034659 + flags = 1 + data = length 418, hash 733A35AE + sample 27: + time = 1069489 + flags = 1 + data = length 418, hash DE46E5D3 + sample 28: + time = 1104319 + flags = 1 + data = length 418, hash 56AB8D37 +track 3: + total output bytes = 81 + sample count = 1 + format 0: + id = 3 + sampleMimeType = text/x-ssa + selectionFlags = 1 + language = und + initializationData: + data = length 90, hash A5E21974 + data = length 470, hash 40E7D996 + sample 0: + time = 0 + flags = 1 + data = length 81, hash F61A8B12 +tracksEnded = true diff --git a/testdata/src/test/assets/extractordumps/mkv/sample_with_ssa_subtitles.mkv.1.dump b/testdata/src/test/assets/extractordumps/mkv/sample_with_ssa_subtitles.mkv.1.dump new file mode 100644 index 0000000000..16df131ed3 --- /dev/null +++ b/testdata/src/test/assets/extractordumps/mkv/sample_with_ssa_subtitles.mkv.1.dump @@ -0,0 +1,283 @@ +seekMap: + isSeekable = true + duration = 1139000 + getPosition(0) = [[timeUs=0, position=6106]] + getPosition(1) = [[timeUs=0, position=6106], [timeUs=67000, position=6106]] + getPosition(569500) = [[timeUs=67000, position=6106]] + getPosition(1139000) = [[timeUs=67000, position=6106]] +numberOfTracks = 3 +track 1: + total output bytes = 89502 + sample count = 30 + format 0: + id = 1 + sampleMimeType = video/avc + width = 1080 + height = 720 + selectionFlags = 1 + language = und + initializationData: + data = length 30, hash F6F3D010 + data = length 10, hash 7A0D0F2B + sample 0: + time = 67000 + flags = 1 + data = length 36477, hash F0F36CFE + sample 1: + time = 134000 + flags = 0 + data = length 5341, hash 40B85E2 + sample 2: + time = 100000 + flags = 0 + data = length 596, hash 357B4D92 + sample 3: + time = 267000 + flags = 0 + data = length 7704, hash A39EDA06 + sample 4: + time = 200000 + flags = 0 + data = length 989, hash 2813C72D + sample 5: + time = 167000 + flags = 0 + data = length 721, hash C50D1C73 + sample 6: + time = 234000 + flags = 0 + data = length 519, hash 65FE1911 + sample 7: + time = 400000 + flags = 0 + data = length 6160, hash E1CAC0EC + sample 8: + time = 334000 + flags = 0 + data = length 953, hash 7160C661 + sample 9: + time = 300000 + flags = 0 + data = length 620, hash 7A7AE07C + sample 10: + time = 367000 + flags = 0 + data = length 405, hash 5CC7F4E7 + sample 11: + time = 500000 + flags = 0 + data = length 4852, hash 9DB6979D + sample 12: + time = 467000 + flags = 0 + data = length 547, hash E31A6979 + sample 13: + time = 434000 + flags = 0 + data = length 570, hash FEC40D00 + sample 14: + time = 634000 + flags = 0 + data = length 5525, hash 7C478F7E + sample 15: + time = 567000 + flags = 0 + data = length 1082, hash DA07059A + sample 16: + time = 534000 + flags = 0 + data = length 807, hash 93478E6B + sample 17: + time = 600000 + flags = 0 + data = length 744, hash 9A8E6026 + sample 18: + time = 767000 + flags = 0 + data = length 4732, hash C73B23C0 + sample 19: + time = 700000 + flags = 0 + data = length 1004, hash 8A19A228 + sample 20: + time = 667000 + flags = 0 + data = length 794, hash 8126022C + sample 21: + time = 734000 + flags = 0 + data = length 645, hash F08300E5 + sample 22: + time = 900000 + flags = 0 + data = length 2684, hash 727FE378 + sample 23: + time = 834000 + flags = 0 + data = length 787, hash 419A7821 + sample 24: + time = 800000 + flags = 0 + data = length 649, hash 5C159346 + sample 25: + time = 867000 + flags = 0 + data = length 509, hash F912D655 + sample 26: + time = 1034000 + flags = 0 + data = length 1226, hash 29815C21 + sample 27: + time = 967000 + flags = 0 + data = length 898, hash D997AD0A + sample 28: + time = 934000 + flags = 0 + data = length 476, hash A0423645 + sample 29: + time = 1000000 + flags = 0 + data = length 486, hash DDF32CBB +track 2: + total output bytes = 12120 + sample count = 29 + format 0: + id = 2 + sampleMimeType = audio/ac3 + channelCount = 1 + sampleRate = 44100 + selectionFlags = 1 + language = und + sample 0: + time = 129000 + flags = 1 + data = length 416, hash 211F2286 + sample 1: + time = 163829 + flags = 1 + data = length 418, hash 77425A86 + sample 2: + time = 198659 + flags = 1 + data = length 418, hash A0FE5CA1 + sample 3: + time = 233489 + flags = 1 + data = length 418, hash 2309B066 + sample 4: + time = 268319 + flags = 1 + data = length 418, hash 928A653B + sample 5: + time = 303149 + flags = 1 + data = length 418, hash 3422F0CB + sample 6: + time = 337979 + flags = 1 + data = length 418, hash EFF43D5B + sample 7: + time = 372809 + flags = 1 + data = length 418, hash FC8093C7 + sample 8: + time = 408000 + flags = 1 + data = length 418, hash CCC08A16 + sample 9: + time = 442829 + flags = 1 + data = length 418, hash 2A6EE863 + sample 10: + time = 477659 + flags = 1 + data = length 418, hash D69A9251 + sample 11: + time = 512489 + flags = 1 + data = length 418, hash BCFB758D + sample 12: + time = 547319 + flags = 1 + data = length 418, hash 11B66799 + sample 13: + time = 582149 + flags = 1 + data = length 418, hash C824D392 + sample 14: + time = 616979 + flags = 1 + data = length 418, hash C167D872 + sample 15: + time = 651809 + flags = 1 + data = length 418, hash 4221C855 + sample 16: + time = 687000 + flags = 1 + data = length 418, hash 4D4FF934 + sample 17: + time = 721829 + flags = 1 + data = length 418, hash 984AA025 + sample 18: + time = 756659 + flags = 1 + data = length 418, hash BB788B46 + sample 19: + time = 791489 + flags = 1 + data = length 418, hash 9EFBFD97 + sample 20: + time = 826319 + flags = 1 + data = length 418, hash DF1A460C + sample 21: + time = 861149 + flags = 1 + data = length 418, hash 2BDB56A + sample 22: + time = 895979 + flags = 1 + data = length 418, hash CA230060 + sample 23: + time = 930809 + flags = 1 + data = length 418, hash D2F19F41 + sample 24: + time = 965000 + flags = 1 + data = length 418, hash AF392D79 + sample 25: + time = 999829 + flags = 1 + data = length 418, hash C5D7F2A3 + sample 26: + time = 1034659 + flags = 1 + data = length 418, hash 733A35AE + sample 27: + time = 1069489 + flags = 1 + data = length 418, hash DE46E5D3 + sample 28: + time = 1104319 + flags = 1 + data = length 418, hash 56AB8D37 +track 3: + total output bytes = 81 + sample count = 1 + format 0: + id = 3 + sampleMimeType = text/x-ssa + selectionFlags = 1 + language = und + initializationData: + data = length 90, hash A5E21974 + data = length 470, hash 40E7D996 + sample 0: + time = 0 + flags = 1 + data = length 81, hash F61A8B12 +tracksEnded = true diff --git a/testdata/src/test/assets/extractordumps/mkv/sample_with_ssa_subtitles.mkv.2.dump b/testdata/src/test/assets/extractordumps/mkv/sample_with_ssa_subtitles.mkv.2.dump new file mode 100644 index 0000000000..16df131ed3 --- /dev/null +++ b/testdata/src/test/assets/extractordumps/mkv/sample_with_ssa_subtitles.mkv.2.dump @@ -0,0 +1,283 @@ +seekMap: + isSeekable = true + duration = 1139000 + getPosition(0) = [[timeUs=0, position=6106]] + getPosition(1) = [[timeUs=0, position=6106], [timeUs=67000, position=6106]] + getPosition(569500) = [[timeUs=67000, position=6106]] + getPosition(1139000) = [[timeUs=67000, position=6106]] +numberOfTracks = 3 +track 1: + total output bytes = 89502 + sample count = 30 + format 0: + id = 1 + sampleMimeType = video/avc + width = 1080 + height = 720 + selectionFlags = 1 + language = und + initializationData: + data = length 30, hash F6F3D010 + data = length 10, hash 7A0D0F2B + sample 0: + time = 67000 + flags = 1 + data = length 36477, hash F0F36CFE + sample 1: + time = 134000 + flags = 0 + data = length 5341, hash 40B85E2 + sample 2: + time = 100000 + flags = 0 + data = length 596, hash 357B4D92 + sample 3: + time = 267000 + flags = 0 + data = length 7704, hash A39EDA06 + sample 4: + time = 200000 + flags = 0 + data = length 989, hash 2813C72D + sample 5: + time = 167000 + flags = 0 + data = length 721, hash C50D1C73 + sample 6: + time = 234000 + flags = 0 + data = length 519, hash 65FE1911 + sample 7: + time = 400000 + flags = 0 + data = length 6160, hash E1CAC0EC + sample 8: + time = 334000 + flags = 0 + data = length 953, hash 7160C661 + sample 9: + time = 300000 + flags = 0 + data = length 620, hash 7A7AE07C + sample 10: + time = 367000 + flags = 0 + data = length 405, hash 5CC7F4E7 + sample 11: + time = 500000 + flags = 0 + data = length 4852, hash 9DB6979D + sample 12: + time = 467000 + flags = 0 + data = length 547, hash E31A6979 + sample 13: + time = 434000 + flags = 0 + data = length 570, hash FEC40D00 + sample 14: + time = 634000 + flags = 0 + data = length 5525, hash 7C478F7E + sample 15: + time = 567000 + flags = 0 + data = length 1082, hash DA07059A + sample 16: + time = 534000 + flags = 0 + data = length 807, hash 93478E6B + sample 17: + time = 600000 + flags = 0 + data = length 744, hash 9A8E6026 + sample 18: + time = 767000 + flags = 0 + data = length 4732, hash C73B23C0 + sample 19: + time = 700000 + flags = 0 + data = length 1004, hash 8A19A228 + sample 20: + time = 667000 + flags = 0 + data = length 794, hash 8126022C + sample 21: + time = 734000 + flags = 0 + data = length 645, hash F08300E5 + sample 22: + time = 900000 + flags = 0 + data = length 2684, hash 727FE378 + sample 23: + time = 834000 + flags = 0 + data = length 787, hash 419A7821 + sample 24: + time = 800000 + flags = 0 + data = length 649, hash 5C159346 + sample 25: + time = 867000 + flags = 0 + data = length 509, hash F912D655 + sample 26: + time = 1034000 + flags = 0 + data = length 1226, hash 29815C21 + sample 27: + time = 967000 + flags = 0 + data = length 898, hash D997AD0A + sample 28: + time = 934000 + flags = 0 + data = length 476, hash A0423645 + sample 29: + time = 1000000 + flags = 0 + data = length 486, hash DDF32CBB +track 2: + total output bytes = 12120 + sample count = 29 + format 0: + id = 2 + sampleMimeType = audio/ac3 + channelCount = 1 + sampleRate = 44100 + selectionFlags = 1 + language = und + sample 0: + time = 129000 + flags = 1 + data = length 416, hash 211F2286 + sample 1: + time = 163829 + flags = 1 + data = length 418, hash 77425A86 + sample 2: + time = 198659 + flags = 1 + data = length 418, hash A0FE5CA1 + sample 3: + time = 233489 + flags = 1 + data = length 418, hash 2309B066 + sample 4: + time = 268319 + flags = 1 + data = length 418, hash 928A653B + sample 5: + time = 303149 + flags = 1 + data = length 418, hash 3422F0CB + sample 6: + time = 337979 + flags = 1 + data = length 418, hash EFF43D5B + sample 7: + time = 372809 + flags = 1 + data = length 418, hash FC8093C7 + sample 8: + time = 408000 + flags = 1 + data = length 418, hash CCC08A16 + sample 9: + time = 442829 + flags = 1 + data = length 418, hash 2A6EE863 + sample 10: + time = 477659 + flags = 1 + data = length 418, hash D69A9251 + sample 11: + time = 512489 + flags = 1 + data = length 418, hash BCFB758D + sample 12: + time = 547319 + flags = 1 + data = length 418, hash 11B66799 + sample 13: + time = 582149 + flags = 1 + data = length 418, hash C824D392 + sample 14: + time = 616979 + flags = 1 + data = length 418, hash C167D872 + sample 15: + time = 651809 + flags = 1 + data = length 418, hash 4221C855 + sample 16: + time = 687000 + flags = 1 + data = length 418, hash 4D4FF934 + sample 17: + time = 721829 + flags = 1 + data = length 418, hash 984AA025 + sample 18: + time = 756659 + flags = 1 + data = length 418, hash BB788B46 + sample 19: + time = 791489 + flags = 1 + data = length 418, hash 9EFBFD97 + sample 20: + time = 826319 + flags = 1 + data = length 418, hash DF1A460C + sample 21: + time = 861149 + flags = 1 + data = length 418, hash 2BDB56A + sample 22: + time = 895979 + flags = 1 + data = length 418, hash CA230060 + sample 23: + time = 930809 + flags = 1 + data = length 418, hash D2F19F41 + sample 24: + time = 965000 + flags = 1 + data = length 418, hash AF392D79 + sample 25: + time = 999829 + flags = 1 + data = length 418, hash C5D7F2A3 + sample 26: + time = 1034659 + flags = 1 + data = length 418, hash 733A35AE + sample 27: + time = 1069489 + flags = 1 + data = length 418, hash DE46E5D3 + sample 28: + time = 1104319 + flags = 1 + data = length 418, hash 56AB8D37 +track 3: + total output bytes = 81 + sample count = 1 + format 0: + id = 3 + sampleMimeType = text/x-ssa + selectionFlags = 1 + language = und + initializationData: + data = length 90, hash A5E21974 + data = length 470, hash 40E7D996 + sample 0: + time = 0 + flags = 1 + data = length 81, hash F61A8B12 +tracksEnded = true diff --git a/testdata/src/test/assets/extractordumps/mkv/sample_with_ssa_subtitles.mkv.3.dump b/testdata/src/test/assets/extractordumps/mkv/sample_with_ssa_subtitles.mkv.3.dump new file mode 100644 index 0000000000..16df131ed3 --- /dev/null +++ b/testdata/src/test/assets/extractordumps/mkv/sample_with_ssa_subtitles.mkv.3.dump @@ -0,0 +1,283 @@ +seekMap: + isSeekable = true + duration = 1139000 + getPosition(0) = [[timeUs=0, position=6106]] + getPosition(1) = [[timeUs=0, position=6106], [timeUs=67000, position=6106]] + getPosition(569500) = [[timeUs=67000, position=6106]] + getPosition(1139000) = [[timeUs=67000, position=6106]] +numberOfTracks = 3 +track 1: + total output bytes = 89502 + sample count = 30 + format 0: + id = 1 + sampleMimeType = video/avc + width = 1080 + height = 720 + selectionFlags = 1 + language = und + initializationData: + data = length 30, hash F6F3D010 + data = length 10, hash 7A0D0F2B + sample 0: + time = 67000 + flags = 1 + data = length 36477, hash F0F36CFE + sample 1: + time = 134000 + flags = 0 + data = length 5341, hash 40B85E2 + sample 2: + time = 100000 + flags = 0 + data = length 596, hash 357B4D92 + sample 3: + time = 267000 + flags = 0 + data = length 7704, hash A39EDA06 + sample 4: + time = 200000 + flags = 0 + data = length 989, hash 2813C72D + sample 5: + time = 167000 + flags = 0 + data = length 721, hash C50D1C73 + sample 6: + time = 234000 + flags = 0 + data = length 519, hash 65FE1911 + sample 7: + time = 400000 + flags = 0 + data = length 6160, hash E1CAC0EC + sample 8: + time = 334000 + flags = 0 + data = length 953, hash 7160C661 + sample 9: + time = 300000 + flags = 0 + data = length 620, hash 7A7AE07C + sample 10: + time = 367000 + flags = 0 + data = length 405, hash 5CC7F4E7 + sample 11: + time = 500000 + flags = 0 + data = length 4852, hash 9DB6979D + sample 12: + time = 467000 + flags = 0 + data = length 547, hash E31A6979 + sample 13: + time = 434000 + flags = 0 + data = length 570, hash FEC40D00 + sample 14: + time = 634000 + flags = 0 + data = length 5525, hash 7C478F7E + sample 15: + time = 567000 + flags = 0 + data = length 1082, hash DA07059A + sample 16: + time = 534000 + flags = 0 + data = length 807, hash 93478E6B + sample 17: + time = 600000 + flags = 0 + data = length 744, hash 9A8E6026 + sample 18: + time = 767000 + flags = 0 + data = length 4732, hash C73B23C0 + sample 19: + time = 700000 + flags = 0 + data = length 1004, hash 8A19A228 + sample 20: + time = 667000 + flags = 0 + data = length 794, hash 8126022C + sample 21: + time = 734000 + flags = 0 + data = length 645, hash F08300E5 + sample 22: + time = 900000 + flags = 0 + data = length 2684, hash 727FE378 + sample 23: + time = 834000 + flags = 0 + data = length 787, hash 419A7821 + sample 24: + time = 800000 + flags = 0 + data = length 649, hash 5C159346 + sample 25: + time = 867000 + flags = 0 + data = length 509, hash F912D655 + sample 26: + time = 1034000 + flags = 0 + data = length 1226, hash 29815C21 + sample 27: + time = 967000 + flags = 0 + data = length 898, hash D997AD0A + sample 28: + time = 934000 + flags = 0 + data = length 476, hash A0423645 + sample 29: + time = 1000000 + flags = 0 + data = length 486, hash DDF32CBB +track 2: + total output bytes = 12120 + sample count = 29 + format 0: + id = 2 + sampleMimeType = audio/ac3 + channelCount = 1 + sampleRate = 44100 + selectionFlags = 1 + language = und + sample 0: + time = 129000 + flags = 1 + data = length 416, hash 211F2286 + sample 1: + time = 163829 + flags = 1 + data = length 418, hash 77425A86 + sample 2: + time = 198659 + flags = 1 + data = length 418, hash A0FE5CA1 + sample 3: + time = 233489 + flags = 1 + data = length 418, hash 2309B066 + sample 4: + time = 268319 + flags = 1 + data = length 418, hash 928A653B + sample 5: + time = 303149 + flags = 1 + data = length 418, hash 3422F0CB + sample 6: + time = 337979 + flags = 1 + data = length 418, hash EFF43D5B + sample 7: + time = 372809 + flags = 1 + data = length 418, hash FC8093C7 + sample 8: + time = 408000 + flags = 1 + data = length 418, hash CCC08A16 + sample 9: + time = 442829 + flags = 1 + data = length 418, hash 2A6EE863 + sample 10: + time = 477659 + flags = 1 + data = length 418, hash D69A9251 + sample 11: + time = 512489 + flags = 1 + data = length 418, hash BCFB758D + sample 12: + time = 547319 + flags = 1 + data = length 418, hash 11B66799 + sample 13: + time = 582149 + flags = 1 + data = length 418, hash C824D392 + sample 14: + time = 616979 + flags = 1 + data = length 418, hash C167D872 + sample 15: + time = 651809 + flags = 1 + data = length 418, hash 4221C855 + sample 16: + time = 687000 + flags = 1 + data = length 418, hash 4D4FF934 + sample 17: + time = 721829 + flags = 1 + data = length 418, hash 984AA025 + sample 18: + time = 756659 + flags = 1 + data = length 418, hash BB788B46 + sample 19: + time = 791489 + flags = 1 + data = length 418, hash 9EFBFD97 + sample 20: + time = 826319 + flags = 1 + data = length 418, hash DF1A460C + sample 21: + time = 861149 + flags = 1 + data = length 418, hash 2BDB56A + sample 22: + time = 895979 + flags = 1 + data = length 418, hash CA230060 + sample 23: + time = 930809 + flags = 1 + data = length 418, hash D2F19F41 + sample 24: + time = 965000 + flags = 1 + data = length 418, hash AF392D79 + sample 25: + time = 999829 + flags = 1 + data = length 418, hash C5D7F2A3 + sample 26: + time = 1034659 + flags = 1 + data = length 418, hash 733A35AE + sample 27: + time = 1069489 + flags = 1 + data = length 418, hash DE46E5D3 + sample 28: + time = 1104319 + flags = 1 + data = length 418, hash 56AB8D37 +track 3: + total output bytes = 81 + sample count = 1 + format 0: + id = 3 + sampleMimeType = text/x-ssa + selectionFlags = 1 + language = und + initializationData: + data = length 90, hash A5E21974 + data = length 470, hash 40E7D996 + sample 0: + time = 0 + flags = 1 + data = length 81, hash F61A8B12 +tracksEnded = true diff --git a/testdata/src/test/assets/extractordumps/mkv/sample_with_ssa_subtitles.mkv.unknown_length.dump b/testdata/src/test/assets/extractordumps/mkv/sample_with_ssa_subtitles.mkv.unknown_length.dump new file mode 100644 index 0000000000..16df131ed3 --- /dev/null +++ b/testdata/src/test/assets/extractordumps/mkv/sample_with_ssa_subtitles.mkv.unknown_length.dump @@ -0,0 +1,283 @@ +seekMap: + isSeekable = true + duration = 1139000 + getPosition(0) = [[timeUs=0, position=6106]] + getPosition(1) = [[timeUs=0, position=6106], [timeUs=67000, position=6106]] + getPosition(569500) = [[timeUs=67000, position=6106]] + getPosition(1139000) = [[timeUs=67000, position=6106]] +numberOfTracks = 3 +track 1: + total output bytes = 89502 + sample count = 30 + format 0: + id = 1 + sampleMimeType = video/avc + width = 1080 + height = 720 + selectionFlags = 1 + language = und + initializationData: + data = length 30, hash F6F3D010 + data = length 10, hash 7A0D0F2B + sample 0: + time = 67000 + flags = 1 + data = length 36477, hash F0F36CFE + sample 1: + time = 134000 + flags = 0 + data = length 5341, hash 40B85E2 + sample 2: + time = 100000 + flags = 0 + data = length 596, hash 357B4D92 + sample 3: + time = 267000 + flags = 0 + data = length 7704, hash A39EDA06 + sample 4: + time = 200000 + flags = 0 + data = length 989, hash 2813C72D + sample 5: + time = 167000 + flags = 0 + data = length 721, hash C50D1C73 + sample 6: + time = 234000 + flags = 0 + data = length 519, hash 65FE1911 + sample 7: + time = 400000 + flags = 0 + data = length 6160, hash E1CAC0EC + sample 8: + time = 334000 + flags = 0 + data = length 953, hash 7160C661 + sample 9: + time = 300000 + flags = 0 + data = length 620, hash 7A7AE07C + sample 10: + time = 367000 + flags = 0 + data = length 405, hash 5CC7F4E7 + sample 11: + time = 500000 + flags = 0 + data = length 4852, hash 9DB6979D + sample 12: + time = 467000 + flags = 0 + data = length 547, hash E31A6979 + sample 13: + time = 434000 + flags = 0 + data = length 570, hash FEC40D00 + sample 14: + time = 634000 + flags = 0 + data = length 5525, hash 7C478F7E + sample 15: + time = 567000 + flags = 0 + data = length 1082, hash DA07059A + sample 16: + time = 534000 + flags = 0 + data = length 807, hash 93478E6B + sample 17: + time = 600000 + flags = 0 + data = length 744, hash 9A8E6026 + sample 18: + time = 767000 + flags = 0 + data = length 4732, hash C73B23C0 + sample 19: + time = 700000 + flags = 0 + data = length 1004, hash 8A19A228 + sample 20: + time = 667000 + flags = 0 + data = length 794, hash 8126022C + sample 21: + time = 734000 + flags = 0 + data = length 645, hash F08300E5 + sample 22: + time = 900000 + flags = 0 + data = length 2684, hash 727FE378 + sample 23: + time = 834000 + flags = 0 + data = length 787, hash 419A7821 + sample 24: + time = 800000 + flags = 0 + data = length 649, hash 5C159346 + sample 25: + time = 867000 + flags = 0 + data = length 509, hash F912D655 + sample 26: + time = 1034000 + flags = 0 + data = length 1226, hash 29815C21 + sample 27: + time = 967000 + flags = 0 + data = length 898, hash D997AD0A + sample 28: + time = 934000 + flags = 0 + data = length 476, hash A0423645 + sample 29: + time = 1000000 + flags = 0 + data = length 486, hash DDF32CBB +track 2: + total output bytes = 12120 + sample count = 29 + format 0: + id = 2 + sampleMimeType = audio/ac3 + channelCount = 1 + sampleRate = 44100 + selectionFlags = 1 + language = und + sample 0: + time = 129000 + flags = 1 + data = length 416, hash 211F2286 + sample 1: + time = 163829 + flags = 1 + data = length 418, hash 77425A86 + sample 2: + time = 198659 + flags = 1 + data = length 418, hash A0FE5CA1 + sample 3: + time = 233489 + flags = 1 + data = length 418, hash 2309B066 + sample 4: + time = 268319 + flags = 1 + data = length 418, hash 928A653B + sample 5: + time = 303149 + flags = 1 + data = length 418, hash 3422F0CB + sample 6: + time = 337979 + flags = 1 + data = length 418, hash EFF43D5B + sample 7: + time = 372809 + flags = 1 + data = length 418, hash FC8093C7 + sample 8: + time = 408000 + flags = 1 + data = length 418, hash CCC08A16 + sample 9: + time = 442829 + flags = 1 + data = length 418, hash 2A6EE863 + sample 10: + time = 477659 + flags = 1 + data = length 418, hash D69A9251 + sample 11: + time = 512489 + flags = 1 + data = length 418, hash BCFB758D + sample 12: + time = 547319 + flags = 1 + data = length 418, hash 11B66799 + sample 13: + time = 582149 + flags = 1 + data = length 418, hash C824D392 + sample 14: + time = 616979 + flags = 1 + data = length 418, hash C167D872 + sample 15: + time = 651809 + flags = 1 + data = length 418, hash 4221C855 + sample 16: + time = 687000 + flags = 1 + data = length 418, hash 4D4FF934 + sample 17: + time = 721829 + flags = 1 + data = length 418, hash 984AA025 + sample 18: + time = 756659 + flags = 1 + data = length 418, hash BB788B46 + sample 19: + time = 791489 + flags = 1 + data = length 418, hash 9EFBFD97 + sample 20: + time = 826319 + flags = 1 + data = length 418, hash DF1A460C + sample 21: + time = 861149 + flags = 1 + data = length 418, hash 2BDB56A + sample 22: + time = 895979 + flags = 1 + data = length 418, hash CA230060 + sample 23: + time = 930809 + flags = 1 + data = length 418, hash D2F19F41 + sample 24: + time = 965000 + flags = 1 + data = length 418, hash AF392D79 + sample 25: + time = 999829 + flags = 1 + data = length 418, hash C5D7F2A3 + sample 26: + time = 1034659 + flags = 1 + data = length 418, hash 733A35AE + sample 27: + time = 1069489 + flags = 1 + data = length 418, hash DE46E5D3 + sample 28: + time = 1104319 + flags = 1 + data = length 418, hash 56AB8D37 +track 3: + total output bytes = 81 + sample count = 1 + format 0: + id = 3 + sampleMimeType = text/x-ssa + selectionFlags = 1 + language = und + initializationData: + data = length 90, hash A5E21974 + data = length 470, hash 40E7D996 + sample 0: + time = 0 + flags = 1 + data = length 81, hash F61A8B12 +tracksEnded = true diff --git a/testdata/src/test/assets/media/mkv/sample_with_null_terminated_srt.mkv b/testdata/src/test/assets/media/mkv/sample_with_null_terminated_srt.mkv new file mode 100644 index 0000000000..8a9298a7bc Binary files /dev/null and b/testdata/src/test/assets/media/mkv/sample_with_null_terminated_srt.mkv differ diff --git a/testdata/src/test/assets/media/mkv/sample_with_null_terminated_ssa_subtitles.mkv b/testdata/src/test/assets/media/mkv/sample_with_null_terminated_ssa_subtitles.mkv new file mode 100644 index 0000000000..8a125b2d50 Binary files /dev/null and b/testdata/src/test/assets/media/mkv/sample_with_null_terminated_ssa_subtitles.mkv differ diff --git a/testdata/src/test/assets/media/mkv/sample_with_ssa_subtitles.mkv b/testdata/src/test/assets/media/mkv/sample_with_ssa_subtitles.mkv new file mode 100644 index 0000000000..5683aa3948 Binary files /dev/null and b/testdata/src/test/assets/media/mkv/sample_with_ssa_subtitles.mkv differ