mirror of
https://github.com/androidx/media.git
synced 2025-04-29 22:36:54 +08:00
Add experimentalSetCodecsToParseWithinGopSampleDependencies to HLS
HLS extractors were missing experimentalSetCodecsToParseWithinGopSampleDependencies prior to this patch. PiperOrigin-RevId: 730878460 (cherry picked from commit da402cfd64ef3ae60607491730f01138177a077b)
This commit is contained in:
parent
a578d43324
commit
521c385d4b
@ -257,6 +257,10 @@
|
||||
{
|
||||
"name": "Apple media playlist (AAC)",
|
||||
"uri": "https://devstreaming-cdn.apple.com/videos/streaming/examples/bipbop_4x3/gear0/prog_index.m3u8"
|
||||
},
|
||||
{
|
||||
"name": "Bitmovin (FMP4)",
|
||||
"uri": "https://bitdash-a.akamaihd.net/content/MI201109210084_1/m3u8s-fmp4/f08e80da-bf1d-4e3d-8899-f0f6155f6efa.m3u8"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
@ -22,6 +22,7 @@ import android.annotation.SuppressLint;
|
||||
import android.net.Uri;
|
||||
import android.text.TextUtils;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.media3.common.C;
|
||||
import androidx.media3.common.FileTypes;
|
||||
import androidx.media3.common.Format;
|
||||
import androidx.media3.common.Metadata;
|
||||
@ -71,6 +72,7 @@ public final class DefaultHlsExtractorFactory implements HlsExtractorFactory {
|
||||
|
||||
private SubtitleParser.Factory subtitleParserFactory;
|
||||
private boolean parseSubtitlesDuringExtraction;
|
||||
private @C.VideoCodecFlags int codecsToParseWithinGopSampleDependencies;
|
||||
|
||||
private final boolean exposeCea608WhenMissingDeclarations;
|
||||
|
||||
@ -179,6 +181,14 @@ public final class DefaultHlsExtractorFactory implements HlsExtractorFactory {
|
||||
return this;
|
||||
}
|
||||
|
||||
@CanIgnoreReturnValue
|
||||
@Override
|
||||
public DefaultHlsExtractorFactory experimentalSetCodecsToParseWithinGopSampleDependencies(
|
||||
@C.VideoCodecFlags int codecsToParseWithinGopSampleDependencies) {
|
||||
this.codecsToParseWithinGopSampleDependencies = codecsToParseWithinGopSampleDependencies;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*
|
||||
@ -242,7 +252,8 @@ public final class DefaultHlsExtractorFactory implements HlsExtractorFactory {
|
||||
parseSubtitlesDuringExtraction,
|
||||
timestampAdjuster,
|
||||
format,
|
||||
muxedCaptionFormats);
|
||||
muxedCaptionFormats,
|
||||
codecsToParseWithinGopSampleDependencies);
|
||||
case FileTypes.TS:
|
||||
return createTsExtractor(
|
||||
payloadReaderFactoryFlags,
|
||||
@ -313,7 +324,8 @@ public final class DefaultHlsExtractorFactory implements HlsExtractorFactory {
|
||||
boolean parseSubtitlesDuringExtraction,
|
||||
TimestampAdjuster timestampAdjuster,
|
||||
Format format,
|
||||
@Nullable List<Format> muxedCaptionFormats) {
|
||||
@Nullable List<Format> muxedCaptionFormats,
|
||||
@C.VideoCodecFlags int codecsToParseWithinGopSampleDependencies) {
|
||||
// Only enable the EMSG TrackOutput if this is the 'variant' track (i.e. the main one) to avoid
|
||||
// creating a separate EMSG track for every audio track in a video stream.
|
||||
@FragmentedMp4Extractor.Flags
|
||||
@ -322,6 +334,9 @@ public final class DefaultHlsExtractorFactory implements HlsExtractorFactory {
|
||||
subtitleParserFactory = SubtitleParser.Factory.UNSUPPORTED;
|
||||
flags |= FragmentedMp4Extractor.FLAG_EMIT_RAW_SUBTITLE_DATA;
|
||||
}
|
||||
flags |=
|
||||
FragmentedMp4Extractor.codecsToParseWithinGopSampleDependenciesAsFlags(
|
||||
codecsToParseWithinGopSampleDependencies);
|
||||
return new FragmentedMp4Extractor(
|
||||
subtitleParserFactory,
|
||||
flags,
|
||||
|
@ -26,6 +26,7 @@ import androidx.media3.exoplayer.analytics.PlayerId;
|
||||
import androidx.media3.extractor.Extractor;
|
||||
import androidx.media3.extractor.ExtractorInput;
|
||||
import androidx.media3.extractor.PositionHolder;
|
||||
import androidx.media3.extractor.mp4.FragmentedMp4Extractor;
|
||||
import androidx.media3.extractor.text.SubtitleParser;
|
||||
import com.google.errorprone.annotations.CanIgnoreReturnValue;
|
||||
import java.io.IOException;
|
||||
@ -101,6 +102,25 @@ public interface HlsExtractorFactory {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the set of video codecs for which within GOP sample dependency information should be
|
||||
* parsed as part of extraction. Defaults to {@code 0} - empty set of codecs.
|
||||
*
|
||||
* <p>Having access to additional sample dependency information can speed up seeking. See {@link
|
||||
* FragmentedMp4Extractor#FLAG_READ_WITHIN_GOP_SAMPLE_DEPENDENCIES}.
|
||||
*
|
||||
* <p>This method is experimental and will be renamed or removed in a future release.
|
||||
*
|
||||
* @param codecsToParseWithinGopSampleDependencies The set of codecs for which to parse within GOP
|
||||
* sample dependency information.
|
||||
* @return This factory, for convenience.
|
||||
*/
|
||||
@CanIgnoreReturnValue
|
||||
default HlsExtractorFactory experimentalSetCodecsToParseWithinGopSampleDependencies(
|
||||
@C.VideoCodecFlags int codecsToParseWithinGopSampleDependencies) {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the output {@link Format} of emitted {@linkplain C#TRACK_TYPE_TEXT text samples} which
|
||||
* were originally in {@code sourceFormat}.
|
||||
|
@ -109,6 +109,7 @@ public final class HlsMediaSource extends BaseMediaSource
|
||||
@Nullable private HlsExtractorFactory extractorFactory;
|
||||
@Nullable private SubtitleParser.Factory subtitleParserFactoryOverride;
|
||||
private boolean parseSubtitlesDuringExtraction;
|
||||
private @C.VideoCodecFlags int codecsToParseWithinGopSampleDependencies;
|
||||
private HlsPlaylistParserFactory playlistParserFactory;
|
||||
private HlsPlaylistTracker.Factory playlistTrackerFactory;
|
||||
private CompositeSequenceableLoaderFactory compositeSequenceableLoaderFactory;
|
||||
@ -220,6 +221,14 @@ public final class HlsMediaSource extends BaseMediaSource
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
@CanIgnoreReturnValue
|
||||
public Factory experimentalSetCodecsToParseWithinGopSampleDependencies(
|
||||
@C.VideoCodecFlags int codecsToParseWithinGopSampleDependencies) {
|
||||
this.codecsToParseWithinGopSampleDependencies = codecsToParseWithinGopSampleDependencies;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the factory from which playlist parsers will be obtained.
|
||||
*
|
||||
@ -396,6 +405,8 @@ public final class HlsMediaSource extends BaseMediaSource
|
||||
extractorFactory.setSubtitleParserFactory(subtitleParserFactoryOverride);
|
||||
}
|
||||
extractorFactory.experimentalParseSubtitlesDuringExtraction(parseSubtitlesDuringExtraction);
|
||||
extractorFactory.experimentalSetCodecsToParseWithinGopSampleDependencies(
|
||||
codecsToParseWithinGopSampleDependencies);
|
||||
HlsExtractorFactory extractorFactory = this.extractorFactory;
|
||||
HlsPlaylistParserFactory playlistParserFactory = this.playlistParserFactory;
|
||||
List<StreamKey> streamKeys = mediaItem.localConfiguration.streamKeys;
|
||||
|
@ -29,6 +29,7 @@ import android.graphics.SurfaceTexture;
|
||||
import android.net.Uri;
|
||||
import android.view.Surface;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.media3.common.C;
|
||||
import androidx.media3.common.MediaItem;
|
||||
import androidx.media3.common.Player;
|
||||
import androidx.media3.datasource.DefaultDataSource;
|
||||
@ -42,6 +43,7 @@ import androidx.media3.exoplayer.source.DefaultMediaSourceFactory;
|
||||
import androidx.media3.exoplayer.source.LoadEventInfo;
|
||||
import androidx.media3.exoplayer.source.MediaLoadData;
|
||||
import androidx.media3.exoplayer.upstream.CmcdConfiguration;
|
||||
import androidx.media3.extractor.DefaultExtractorsFactory;
|
||||
import androidx.media3.test.utils.CapturingRenderersFactory;
|
||||
import androidx.media3.test.utils.DumpFileAsserts;
|
||||
import androidx.media3.test.utils.FakeClock;
|
||||
@ -464,6 +466,39 @@ public final class HlsPlaybackTest {
|
||||
assertThat(loadCompletedDataSpecUris).containsExactlyElementsIn(loadStartedUris);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void playVideo_usingWithinGopSampleDependencies_withSeek() throws Exception {
|
||||
Context applicationContext = ApplicationProvider.getApplicationContext();
|
||||
CapturingRenderersFactory capturingRenderersFactory =
|
||||
new CapturingRenderersFactory(applicationContext);
|
||||
DefaultMediaSourceFactory defaultMediaSourceFactory =
|
||||
new DefaultMediaSourceFactory(applicationContext, new DefaultExtractorsFactory());
|
||||
defaultMediaSourceFactory.experimentalSetCodecsToParseWithinGopSampleDependencies(
|
||||
C.VIDEO_CODEC_FLAG_H264);
|
||||
ExoPlayer player =
|
||||
new ExoPlayer.Builder(
|
||||
applicationContext, capturingRenderersFactory, defaultMediaSourceFactory)
|
||||
.setClock(new FakeClock(/* isAutoAdvancing= */ true))
|
||||
.build();
|
||||
Surface surface = new Surface(new SurfaceTexture(/* texName= */ 1));
|
||||
player.setVideoSurface(surface);
|
||||
PlaybackOutput playbackOutput = PlaybackOutput.register(player, capturingRenderersFactory);
|
||||
|
||||
player.setMediaItem(
|
||||
MediaItem.fromUri("asset:///media/hls/standalone-webvtt/multivariant_playlist.m3u8"));
|
||||
player.seekTo(500L);
|
||||
player.prepare();
|
||||
player.play();
|
||||
advance(player).untilState(Player.STATE_ENDED);
|
||||
player.release();
|
||||
surface.release();
|
||||
|
||||
DumpFileAsserts.assertOutput(
|
||||
applicationContext,
|
||||
playbackOutput,
|
||||
"playbackdumps/hls/standalone-webvtt-optimized-seek.dump");
|
||||
}
|
||||
|
||||
private static class AnalyticsListenerImpl implements AnalyticsListener {
|
||||
|
||||
@Nullable private LoadEventInfo loadErrorEventInfo;
|
||||
|
@ -0,0 +1,219 @@
|
||||
MediaCodecAdapter (exotest.video.avc):
|
||||
inputBuffers:
|
||||
count = 24
|
||||
input buffer #0:
|
||||
timeUs = 1000000000000
|
||||
contents = length 36692, hash D216076E
|
||||
input buffer #1:
|
||||
timeUs = 1000000066733
|
||||
contents = length 5312, hash D45D3CA0
|
||||
input buffer #2:
|
||||
timeUs = 1000000200200
|
||||
contents = length 7735, hash 4490F110
|
||||
input buffer #3:
|
||||
timeUs = 1000000133466
|
||||
contents = length 987, hash 560B5036
|
||||
input buffer #4:
|
||||
timeUs = 1000000333666
|
||||
contents = length 6061, hash 736C72B2
|
||||
input buffer #5:
|
||||
timeUs = 1000000266933
|
||||
contents = length 992, hash FE132F23
|
||||
input buffer #6:
|
||||
timeUs = 1000000433766
|
||||
contents = length 4899, hash F72F86A1
|
||||
input buffer #7:
|
||||
timeUs = 1000000400400
|
||||
contents = length 568, hash 519A8E50
|
||||
input buffer #8:
|
||||
timeUs = 1000000567233
|
||||
contents = length 5450, hash F06EC4AA
|
||||
input buffer #9:
|
||||
timeUs = 1000000500500
|
||||
contents = length 1051, hash 92DFA63A
|
||||
input buffer #10:
|
||||
timeUs = 1000000533866
|
||||
contents = length 781, hash 36BE495B
|
||||
input buffer #11:
|
||||
timeUs = 1000000700700
|
||||
contents = length 4725, hash AC0C8CD3
|
||||
input buffer #12:
|
||||
timeUs = 1000000633966
|
||||
contents = length 1022, hash 5D8BFF34
|
||||
input buffer #13:
|
||||
timeUs = 1000000600600
|
||||
contents = length 790, hash 99413A99
|
||||
input buffer #14:
|
||||
timeUs = 1000000667333
|
||||
contents = length 610, hash 5E129290
|
||||
input buffer #15:
|
||||
timeUs = 1000000834166
|
||||
contents = length 2751, hash 769974CB
|
||||
input buffer #16:
|
||||
timeUs = 1000000767433
|
||||
contents = length 745, hash B78A477A
|
||||
input buffer #17:
|
||||
timeUs = 1000000734066
|
||||
contents = length 621, hash CF741E7A
|
||||
input buffer #18:
|
||||
timeUs = 1000000800800
|
||||
contents = length 505, hash 1DB4894E
|
||||
input buffer #19:
|
||||
timeUs = 1000000967633
|
||||
contents = length 1268, hash C15348DC
|
||||
input buffer #20:
|
||||
timeUs = 1000000900900
|
||||
contents = length 880, hash C2DE85D0
|
||||
input buffer #21:
|
||||
timeUs = 1000000867533
|
||||
contents = length 530, hash C98BC6A8
|
||||
input buffer #22:
|
||||
timeUs = 1000000934266
|
||||
contents = length 568, hash 4FE5C8EA
|
||||
input buffer #23:
|
||||
timeUs = 0
|
||||
flags = 4
|
||||
contents = length 0, hash 1
|
||||
outputBuffers:
|
||||
count = 23
|
||||
output buffer #0:
|
||||
timeUs = 1000000000000
|
||||
size = 36692
|
||||
rendered = false
|
||||
output buffer #1:
|
||||
timeUs = 1000000066733
|
||||
size = 5312
|
||||
rendered = false
|
||||
output buffer #2:
|
||||
timeUs = 1000000200200
|
||||
size = 7735
|
||||
rendered = false
|
||||
output buffer #3:
|
||||
timeUs = 1000000133466
|
||||
size = 987
|
||||
rendered = false
|
||||
output buffer #4:
|
||||
timeUs = 1000000333666
|
||||
size = 6061
|
||||
rendered = false
|
||||
output buffer #5:
|
||||
timeUs = 1000000266933
|
||||
size = 992
|
||||
rendered = false
|
||||
output buffer #6:
|
||||
timeUs = 1000000433766
|
||||
size = 4899
|
||||
rendered = false
|
||||
output buffer #7:
|
||||
timeUs = 1000000400400
|
||||
size = 568
|
||||
rendered = false
|
||||
output buffer #8:
|
||||
timeUs = 1000000567233
|
||||
size = 5450
|
||||
rendered = true
|
||||
output buffer #9:
|
||||
timeUs = 1000000500500
|
||||
size = 1051
|
||||
rendered = true
|
||||
output buffer #10:
|
||||
timeUs = 1000000533866
|
||||
size = 781
|
||||
rendered = true
|
||||
output buffer #11:
|
||||
timeUs = 1000000700700
|
||||
size = 4725
|
||||
rendered = true
|
||||
output buffer #12:
|
||||
timeUs = 1000000633966
|
||||
size = 1022
|
||||
rendered = true
|
||||
output buffer #13:
|
||||
timeUs = 1000000600600
|
||||
size = 790
|
||||
rendered = true
|
||||
output buffer #14:
|
||||
timeUs = 1000000667333
|
||||
size = 610
|
||||
rendered = true
|
||||
output buffer #15:
|
||||
timeUs = 1000000834166
|
||||
size = 2751
|
||||
rendered = true
|
||||
output buffer #16:
|
||||
timeUs = 1000000767433
|
||||
size = 745
|
||||
rendered = true
|
||||
output buffer #17:
|
||||
timeUs = 1000000734066
|
||||
size = 621
|
||||
rendered = true
|
||||
output buffer #18:
|
||||
timeUs = 1000000800800
|
||||
size = 505
|
||||
rendered = true
|
||||
output buffer #19:
|
||||
timeUs = 1000000967633
|
||||
size = 1268
|
||||
rendered = true
|
||||
output buffer #20:
|
||||
timeUs = 1000000900900
|
||||
size = 880
|
||||
rendered = true
|
||||
output buffer #21:
|
||||
timeUs = 1000000867533
|
||||
size = 530
|
||||
rendered = true
|
||||
output buffer #22:
|
||||
timeUs = 1000000934266
|
||||
size = 568
|
||||
rendered = true
|
||||
TextOutput:
|
||||
Subtitle[0]:
|
||||
presentationTimeUs = 500000
|
||||
Cues = []
|
||||
Subtitle[1]:
|
||||
presentationTimeUs = 567000
|
||||
Cue[0]:
|
||||
text = This is the first subtitle.
|
||||
textAlignment = ALIGN_CENTER
|
||||
line = -1.0
|
||||
lineType = 1
|
||||
lineAnchor = 0
|
||||
position = 0.5
|
||||
positionAnchor = 1
|
||||
size = 1.0
|
||||
Subtitle[2]:
|
||||
presentationTimeUs = 667000
|
||||
Cue[0]:
|
||||
text = This is the first subtitle.
|
||||
textAlignment = ALIGN_CENTER
|
||||
line = -1.0
|
||||
lineType = 1
|
||||
lineAnchor = 0
|
||||
position = 0.5
|
||||
positionAnchor = 1
|
||||
size = 1.0
|
||||
Cue[1]:
|
||||
text = This is the second subtitle.
|
||||
textAlignment = ALIGN_CENTER
|
||||
line = -2.0
|
||||
lineType = 1
|
||||
lineAnchor = 0
|
||||
position = 0.5
|
||||
positionAnchor = 1
|
||||
size = 1.0
|
||||
Subtitle[3]:
|
||||
presentationTimeUs = 801000
|
||||
Cue[0]:
|
||||
text = This is the second subtitle.
|
||||
textAlignment = ALIGN_CENTER
|
||||
line = -1.0
|
||||
lineType = 1
|
||||
lineAnchor = 0
|
||||
position = 0.5
|
||||
positionAnchor = 1
|
||||
size = 1.0
|
||||
Subtitle[4]:
|
||||
presentationTimeUs = 951000
|
||||
Cues = []
|
Loading…
x
Reference in New Issue
Block a user