ExoPlayer는 여러 컨테이너 형식을 사용하여 HLS를 지원합니다. 포함된 오디오 및 동영상 샘플 형식도 지원되어야 합니다 (자세한 내용은 샘플 형식 섹션 참고). HLS 콘텐츠 제작자는 이 블로그 게시물에 설명된 대로 고품질 HLS 스트림을 생성하는 것이 좋습니다.
| 기능 | 지원됨 | 비고 |
|---|---|---|
| 컨테이너 | ||
| MPEG-TS | 예 | |
| FMP4/CMAF | 예 | |
| ADTS (AAC) | 예 | |
| MP3 | 예 | |
| 자막 | ||
| CEA-608 | 예 | |
| CEA-708 | 예 | |
| WebVTT | 예 | |
| 메타데이터 | ||
| ID3 | 예 | |
| SCTE-35 | 아니요 | |
| 콘텐츠 보호 | ||
| AES-128 | 예 | |
| 샘플 AES-128 | 아니요 | |
| Widevine | 예 | API 19 이상 ('cenc' 스킴) 및 25 이상 ('cbcs' 스킴) |
| PlayReady SL2000 | 예 | Android TV만 |
| 서버 제어 | ||
| 델타 업데이트 | 예 | |
| 재생목록 다시 로드를 차단함 | 예 | |
| 미리 로드 힌트의 로드 차단 | 예 | 길이가 정의되지 않은 바이트 범위는 제외 |
| 광고 삽입 | ||
| 서버 안내 광고 삽입(전면 광고) | 일부만 | X-ASSET-URI가 포함된 VOD만 해당됩니다.
라이브 스트림과 X-ASSET-LIST은 나중에 추가될 예정입니다. |
| IMA 서버 측 및 클라이언트 측 광고 | 예 | 광고 삽입 가이드 |
| 라이브 재생 | ||
| 일반 라이브 재생 | 예 | |
| 짧은 지연 시간 HLS (Apple) | 예 | |
| 짧은 지연 시간 HLS (커뮤니티) | 아니요 | |
| Common Media Client Data CMCD | 예 | CMCD 통합 가이드 |
MediaItem 사용
HLS 스트림을 재생하려면 HLS 모듈에 종속되어야 합니다.
Kotlin
implementation("androidx.media3:media3-exoplayer-hls:1.8.0")
Groovy
implementation "androidx.media3:media3-exoplayer-hls:1.8.0"
그런 다음 HLS 재생목록 URI의 MediaItem를 만들어 플레이어에 전달할 수 있습니다.
Kotlin
// Create a player instance.
val player = ExoPlayer.Builder(context).build()
// Set the media item to be played.
player.setMediaItem(MediaItem.fromUri(hlsUri))
// Prepare the player.
player.prepare()
자바
// Create a player instance.
ExoPlayer player = new ExoPlayer.Builder(context).build();
// Set the media item to be played.
player.setMediaItem(MediaItem.fromUri(hlsUri));
// Prepare the player.
player.prepare();
URI가 .m3u8로 끝나지 않으면 MediaItem.Builder의 setMimeType에 MimeTypes.APPLICATION_M3U8를 전달하여 콘텐츠 유형을 명시적으로 나타낼 수 있습니다.
미디어 항목의 URI는 미디어 재생목록이나 다변형 재생목록을 가리킬 수 있습니다. URI가 여러 #EXT-X-STREAM-INF 태그를 선언하는 멀티 변형 재생목록을 가리키는 경우 ExoPlayer는 사용 가능한 대역폭과 기기 기능을 모두 고려하여 변형 간에 자동으로 적응합니다.
HlsMediaSource 사용
더 많은 맞춤설정 옵션을 사용하려면 MediaItem 대신 HlsMediaSource를 만들어 플레이어에 직접 전달하면 됩니다.
Kotlin
// Create a data source factory.
val dataSourceFactory: DataSource.Factory = DefaultHttpDataSource.Factory()
// Create a HLS media source pointing to a playlist uri.
val hlsMediaSource =
HlsMediaSource.Factory(dataSourceFactory).createMediaSource(MediaItem.fromUri(hlsUri))
// Create a player instance.
val player = ExoPlayer.Builder(context).build()
// Set the HLS media source as the playlist with a single media item.
player.setMediaSource(hlsMediaSource)
// Prepare the player.
player.prepare()
자바
// Create a data source factory.
DataSource.Factory dataSourceFactory = new DefaultHttpDataSource.Factory();
// Create a HLS media source pointing to a playlist uri.
HlsMediaSource hlsMediaSource =
new HlsMediaSource.Factory(dataSourceFactory).createMediaSource(MediaItem.fromUri(hlsUri));
// Create a player instance.
ExoPlayer player = new ExoPlayer.Builder(context).build();
// Set the HLS media source as the playlist with a single media item.
player.setMediaSource(hlsMediaSource);
// Prepare the player.
player.prepare();
매니페스트 액세스
Player.getCurrentManifest를 호출하여 현재 매니페스트를 가져올 수 있습니다.
HLS의 경우 반환된 객체를 HlsManifest로 변환해야 합니다. 매니페스트가 로드될 때마다 Player.Listener의 onTimelineChanged 콜백도 호출됩니다. 온디맨드 콘텐츠의 경우 한 번 발생하며 라이브 콘텐츠의 경우 여러 번 발생할 수 있습니다. 다음 코드 스니펫은 매니페스트가 로드될 때마다 앱이 작업을 실행하는 방법을 보여줍니다.
Kotlin
player.addListener(
object : Player.Listener {
override fun onTimelineChanged(timeline: Timeline, @TimelineChangeReason reason: Int) {
val manifest = player.currentManifest
if (manifest is HlsManifest) {
// Do something with the manifest.
}
}
}
)
자바
player.addListener(
new Player.Listener() {
@Override
public void onTimelineChanged(
Timeline timeline, @Player.TimelineChangeReason int reason) {
Object manifest = player.getCurrentManifest();
if (manifest != null) {
HlsManifest hlsManifest = (HlsManifest) manifest;
// Do something with the manifest.
}
}
});
전면 광고가 포함된 HLS 스트림 재생
HLS 사양은 미디어 재생목록에 광고 정보를 포함하는 데 사용할 수 있는 HLS 광고를 정의합니다. ExoPlayer는 기본적으로 이러한 광고를 무시합니다. HlsInterstitialsAdsLoader을 사용하여 지원을 추가할 수 있습니다. 처음부터 사양의 모든 기능을 지원하지는 않습니다. 스트림 지원이 누락된 경우 GitHub에서 문제를 제출하여 알려주시고 스트림 URI를 보내주시면 스트림 지원을 추가할 수 있습니다.
재생목록 API와 함께 MediaItem 사용
인터스티셜로 HLS 스트림을 재생하는 가장 편리한 방법은 HlsInterstitialsAdsLoader.AdsMediaSourceFactory로 ExoPlayer 인스턴스를 빌드하는 것입니다.
이를 통해 Player 인터페이스의 MediaItem 기반 재생목록 API를 사용하여 HLS 전면 광고를 재생할 수 있습니다.
플레이어 인스턴스를 빌드할 때 ExoPlayer의 MediaSource.Factory을 빌더에 삽입할 수 있습니다.
Kotlin
hlsInterstitialsAdsLoader = HlsInterstitialsAdsLoader(context)
// Create a MediaSource.Factory for HLS streams with interstitials.
val hlsMediaSourceFactory =
HlsInterstitialsAdsLoader.AdsMediaSourceFactory(
hlsInterstitialsAdsLoader,
playerView,
DefaultMediaSourceFactory(context),
)
// Build player with interstitials media source factory
player =
ExoPlayer.Builder(context)
.setMediaSourceFactory(hlsMediaSourceFactory)
.build()
// Set the player on the ads loader.
hlsInterstitialsAdsLoader.setPlayer(player)
playerView.setPlayer(player)
자바
hlsInterstitialsAdsLoader = new HlsInterstitialsAdsLoader(context);
// Create a MediaSource.Factory for HLS streams with interstitials.
MediaSource.Factory hlsMediaSourceFactory =
new HlsInterstitialsAdsLoader.AdsMediaSourceFactory(
hlsInterstitialsAdsLoader, playerView, new DefaultMediaSourceFactory(context));
// Build player with interstitials media source factory
player =
new ExoPlayer.Builder(context)
.setMediaSourceFactory(hlsMediaSourceFactory)
.build();
// Set the player on the ads loader.
hlsInterstitialsAdsLoader.setPlayer(player);
playerView.setPlayer(player);
이러한 플레이어 설정으로 HLS 전면 광고를 재생하려면 플레이어에서 AdsConfiguration가 있는 미디어 항목을 설정하면 됩니다.
Kotlin
player.setMediaItem(
MediaItem.Builder()
.setUri("https://www.example.com/media.m3u8")
.setAdsConfiguration(
MediaItem.AdsConfiguration.Builder("hls://interstitials".toUri())
.setAdsId("ad-tag-0") // must be unique within playlist
.build())
.build())
player.prepare()
player.play()
자바
player.setMediaItem(
new MediaItem.Builder()
.setUri("https://www.example.com/media.m3u8")
.setAdsConfiguration(
new AdsConfiguration.Builder(Uri.parse("hls://interstitials"))
.setAdsId("ad-tag-0") // must be unique within playlist
.build())
.build());
player.prepare();
player.play();
미디어 소스 기반 API 사용
또는 기본 미디어 소스 팩토리를 재정의하지 않고 ExoPlayer 인스턴스를 빌드할 수 있습니다. 전면 광고를 지원하기 위해 앱은 HlsInterstitialsAdsLoader.AdsMediaSourceFactory를 직접 사용하여 MediaSource를 만들고 미디어 소스 기반 재생목록 API를 사용하여 ExoPlayer에 제공할 수 있습니다.
Kotlin
hlsInterstitialsAdsLoader = HlsInterstitialsAdsLoader(context)
// Create a MediaSource.Factory for HLS streams with interstitials.
val hlsMediaSourceFactory =
HlsInterstitialsAdsLoader.AdsMediaSourceFactory(hlsInterstitialsAdsLoader, playerView, context)
// Build player with default media source factory.
player = ExoPlayer.Builder(context).build();
// Create an media source from an HLS media item with ads configuration.
val mediaSource =
hlsMediaSourceFactory.createMediaSource(
MediaItem.Builder()
.setUri("https://www.example.com/media.m3u8")
.setAdsConfiguration(
MediaItem.AdsConfiguration.Builder("hls://interstitials".toUri())
.setAdsId("ad-tag-0")
.build()
)
.build()
)
// Set the media source on the player.
player.setMediaSource(mediaSource)
player.prepare()
player.play()
자바
HlsInterstitialsAdsLoader hlsInterstitialsAdsLoader = new HlsInterstitialsAdsLoader(context);
// Create a MediaSource.Factory for HLS streams with interstitials.
MediaSource.Factory hlsMediaSourceFactory =
new HlsInterstitialsAdsLoader.AdsMediaSourceFactory(
hlsInterstitialsAdsLoader, playerView, context);
// Build player with default media source factory.
player = new ExoPlayer.Builder(context).build();
// Create an media source from an HLS media item with ads configuration.
MediaSource mediaSource =
hlsMediaSourceFactory.createMediaSource(
new MediaItem.Builder()
.setUri("https://www.example.com/media.m3u8")
.setAdsConfiguration(
new MediaItem.AdsConfiguration.Builder(Uri.parse("hls://interstitials"))
.setAdsId("ad-tag-0")
.build())
.build());
// Set the media source on the player.
exoPlayer.setMediaSource(mediaSource);
exoPlayer.prepare();
exoPlayer.play();
광고 이벤트 수신 대기
Listener를 HlsInterstitialsAdsLoader에 추가하여 HLS 광고 재생과 관련된 상태 변경에 관한 이벤트를 모니터링할 수 있습니다. 이를 통해 앱 또는 SDK는 재생된 광고, 로드되는 애셋 목록, 준비 중인 광고 미디어 소스를 추적하거나 애셋 목록 로드 및 광고 준비 오류를 감지할 수 있습니다. 또한 광고 미디어 소스에서 내보낸 메타데이터를 수신하여 세부적인 광고 재생을 확인하거나 광고 재생 진행 상황을 추적할 수 있습니다.
Kotlin
class AdsLoaderListener : HlsInterstitialsAdsLoader.Listener {
override fun onStart(mediaItem: MediaItem, adsId: Any, adViewProvider: AdViewProvider) {
// Do something when HLS media item with interstitials is started.
}
override fun onMetadata(
mediaItem: MediaItem,
adsId: Any,
adGroupIndex: Int,
adIndexInAdGroup: Int,
metadata: Metadata,
) {
// Do something with metadata that is emitted by the ad media source of the given ad.
}
override fun onAdCompleted(
mediaItem: MediaItem,
adsId: Any,
adGroupIndex: Int,
adIndexInAdGroup: Int,
) {
// Do something when ad completed playback.
}
// ... See JavaDoc for further callbacks of HlsInterstitialsAdsLoader.Listener.
override fun onStop(mediaItem: MediaItem, adsId: Any, adPlaybackState: AdPlaybackState) {
// Do something with the resulting ad playback state when stopped.
}
}
자바
private class AdsLoaderListener
implements HlsInterstitialsAdsLoader.Listener {
// implement HlsInterstitialsAdsLoader.Listener
@Override
public void onStart(MediaItem mediaItem, Object adsId, AdViewProvider adViewProvider) {
// Do something when HLS media item with interstitials is started.
}
@Override
public void onMetadata(
MediaItem mediaItem,
Object adsId,
int adGroupIndex,
int adIndexInAdGroup,
Metadata metadata) {
// Do something with metadata that is emitted by the ad media source of the given ad.
}
@Override
public void onAdCompleted(
MediaItem mediaItem, Object adsId, int adGroupIndex, int adIndexInAdGroup) {
// Do something when ad completed playback.
}
// ... See JavaDoc for further callbacks
@Override
public void onStop(MediaItem mediaItem, Object adsId, AdPlaybackState adPlaybackState) {
// Do something with the resulting ad playback state when stopped.
}
}
사용 가능한 모든 콜백에 관한 자세한 문서는 HlsInterstitialsAdsLoader.Listener의 JavaDoc을 참고하세요.
그런 다음 리스너를 광고 로더에 추가할 수 있습니다.
Kotlin
val listener = AdsLoaderListener()
// Add the listener to the ads loader to receive ad loader events.
hlsInterstitialsAdsLoader.addListener(listener)
자바
AdsLoaderListener listener = new AdsLoaderListener();
// Add the listener to the ads loader to receive ad loader events.
hlsInterstitialsAdsLoader.addListener(listener);
HlsInterstitialsAdsLoader 수명 주기
HlsInterstitialsAdsLoader 또는 HlsInterstitialsAdsLoader.AdsMediaSourceFactory 인스턴스는 광고를 로드해야 하는 여러 미디어 소스를 만드는 여러 플레이어 인스턴스에 재사용할 수 있습니다.
인스턴스는 예를 들어 Activity의 onCreate 메서드에서 생성한 다음 여러 플레이어 인스턴스에 재사용할 수 있습니다. 동시에 단일 플레이어 인스턴스에서 사용되는 한 작동합니다. 이는 앱이 백그라운드로 전환되고 플레이어 인스턴스가 소멸된 후 앱이 다시 포그라운드로 전환될 때 새 인스턴스가 생성되는 일반적인 사용 사례에 유용합니다.
광고 재생 상태를 사용한 재생 재개
사용자가 광고를 다시 시청하지 않도록 하려면 플레이어를 다시 만들 때 광고 재생 상태를 저장하고 복원하면 됩니다. 플레이어가 해제되려고 할 때 getAdsResumptionStates()를 호출하고 반환된 AdsResumptionState 객체를 저장하면 됩니다. 플레이어가 다시 생성되면 광고 로더 인스턴스에서 addAdResumptionState()를 호출하여 상태를 복원할 수 있습니다. AdsResumptionState는 번들로 묶을 수 있으므로 Activity의 onSaveInstanceState 번들에 저장할 수 있습니다. 광고 재개는 VOD 스트림에서만 지원됩니다.
Kotlin
companion object {
const val ADS_RESUMPTION_STATE_KEY = "ads_resumption_state"
}
private var hlsInterstitialsAdsLoader: HlsInterstitialsAdsLoader? = null
private var playerView: PlayerView? = null
private var player: ExoPlayer? = null
private var adsResumptionStates: List<HlsInterstitialsAdsLoader.AdsResumptionState>? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Create the ads loader instance.
hlsInterstitialsAdsLoader = HlsInterstitialsAdsLoader(this)
// Restore ad resumption states.
savedInstanceState?.getParcelableArrayList<Bundle>(ADS_RESUMPTION_STATE_KEY)?.let { bundles ->
adsResumptionStates =
bundles.map { HlsInterstitialsAdsLoader.AdsResumptionState.fromBundle(it) }
}
}
override fun onStart() {
super.onStart()
// Build a player and set it on the ads loader.
initializePlayer()
hlsInterstitialsAdsLoader?.setPlayer(player)
// Add any stored ad resumption states to the ads loader.
adsResumptionStates?.forEach { hlsInterstitialsAdsLoader?.addAdResumptionState(it) }
adsResumptionStates = null // Consume the states
}
override fun onStop() {
super.onStop()
// Get ad resumption states.
adsResumptionStates = hlsInterstitialsAdsLoader?.adsResumptionStates
releasePlayer()
}
override fun onDestroy() {
// Release the ads loader when not used anymore.
hlsInterstitialsAdsLoader?.release()
hlsInterstitialsAdsLoader = null
super.onDestroy()
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
// Store the ad resumption states.
adsResumptionStates?.let {
outState.putParcelableArrayList(
ADS_RESUMPTION_STATE_KEY,
ArrayList(it.map(HlsInterstitialsAdsLoader.AdsResumptionState::toBundle)),
)
}
}
fun initializePlayer() {
if (player == null) {
// Create a media source factory for HLS streams.
val hlsMediaSourceFactory =
HlsInterstitialsAdsLoader.AdsMediaSourceFactory(
checkNotNull(hlsInterstitialsAdsLoader),
playerView!!,
/* context= */ this,
)
// Build player with interstitials media source
player =
ExoPlayer.Builder(/* context= */ this).setMediaSourceFactory(hlsMediaSourceFactory).build()
// Set the player on the ads loader.
hlsInterstitialsAdsLoader?.setPlayer(player)
playerView?.player = player
}
// Use a media item with an HLS stream URI, an ad tag URI and ads ID.
player?.setMediaItem(
MediaItem.Builder()
.setUri("https://www.example.com/media.m3u8")
.setMimeType(MimeTypes.APPLICATION_M3U8)
.setAdsConfiguration(
MediaItem.AdsConfiguration.Builder("hls://interstitials".toUri())
.setAdsId("ad-tag-0") // must be unique within ExoPlayer playlist
.build()
)
.build()
)
player?.prepare()
player?.play()
}
fun releasePlayer() {
player?.release()
player = null
hlsInterstitialsAdsLoader?.setPlayer(null)
playerView?.setPlayer(null)
}
자바
public static final String ADS_RESUMPTION_STATE_KEY = "ads_resumption_state";
@Nullable private HlsInterstitialsAdsLoader hlsInterstitialsAdsLoader;
@Nullable private PlayerView playerView;
@Nullable private ExoPlayer player;
private List<HlsInterstitialsAdsLoader.AdsResumptionState> adsResumptionStates;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Create the ads loader instance.
hlsInterstitialsAdsLoader = new HlsInterstitialsAdsLoader(this);
// Restore ad resumption states.
if (savedInstanceState != null) {
ArrayList<Bundle> bundles =
savedInstanceState.getParcelableArrayList(ADS_RESUMPTION_STATE_KEY);
if (bundles != null) {
adsResumptionStates = new ArrayList<>();
for (Bundle bundle : bundles) {
adsResumptionStates.add(
HlsInterstitialsAdsLoader.AdsResumptionState.fromBundle(bundle));
}
}
}
}
@Override
protected void onStart() {
super.onStart();
// Build a player and set it on the ads loader.
initializePlayer();
// Add any stored ad resumption states to the ads loader.
if (adsResumptionStates != null) {
for (HlsInterstitialsAdsLoader.AdsResumptionState state : adsResumptionStates) {
hlsInterstitialsAdsLoader.addAdResumptionState(state);
}
adsResumptionStates = null; // Consume the states
}
}
@Override
protected void onStop() {
super.onStop();
// Get ad resumption states before releasing the player.
adsResumptionStates = hlsInterstitialsAdsLoader.getAdsResumptionStates();
releasePlayer();
}
@Override
protected void onDestroy() {
// Release the ads loader when not used anymore.
if (hlsInterstitialsAdsLoader != null) {
hlsInterstitialsAdsLoader.release();
hlsInterstitialsAdsLoader = null;
}
super.onDestroy();
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
// Store the ad resumption states.
if (adsResumptionStates != null) {
ArrayList<Bundle> bundles = new ArrayList<>();
for (HlsInterstitialsAdsLoader.AdsResumptionState state : adsResumptionStates) {
bundles.add(state.toBundle());
}
outState.putParcelableArrayList(ADS_RESUMPTION_STATE_KEY, bundles);
}
}
private void initializePlayer() {
if (player == null) {
// Create a media source factory for HLS streams.
MediaSource.Factory hlsMediaSourceFactory =
new HlsInterstitialsAdsLoader.AdsMediaSourceFactory(
checkNotNull(hlsInterstitialsAdsLoader), playerView, /* context= */ this);
// Build player with interstitials media source
player =
new ExoPlayer.Builder(/* context= */ this)
.setMediaSourceFactory(hlsMediaSourceFactory)
.build();
// Set the player on the ads loader.
hlsInterstitialsAdsLoader.setPlayer(player);
playerView.setPlayer(player);
}
// Use a media item with an HLS stream URI, an ad tag URI and ads ID.
player.setMediaItem(
new MediaItem.Builder()
.setUri("https://www.example.com/media.m3u8")
.setMimeType(MimeTypes.APPLICATION_M3U8)
.setAdsConfiguration(
new MediaItem.AdsConfiguration.Builder(Uri.parse("hls://interstitials"))
.setAdsId("ad-tag-0") // must be unique within ExoPlayer playlist
.build())
.build());
player.prepare();
player.play();
}
private void releasePlayer() {
if (player != null) {
player.release();
player = null;
}
if (hlsInterstitialsAdsLoader != null) {
hlsInterstitialsAdsLoader.setPlayer(null);
}
if (playerView != null) {
playerView.setPlayer(null);
}
}
removeAdResumptionState(Object adsId)로 개별 재개 상태를 삭제하거나 clearAllAdResumptionStates()로 모두 삭제할 수도 있습니다.
일반적으로 광고 로더에서 다음 플레이어 인스턴스를 설정하기 전에 이전 플레이어 인스턴스를 해제해야 합니다. 광고 로더 자체가 출시되면 광고 로더를 더 이상 사용할 수 없습니다.
재생 맞춤설정
ExoPlayer는 앱의 요구사항에 맞게 재생 환경을 맞춤설정할 수 있는 여러 방법을 제공합니다. 예는 맞춤설정 페이지를 참고하세요.
청크 없는 준비 사용 중지
기본적으로 ExoPlayer는 청크 없는 준비를 사용합니다. 즉, ExoPlayer는 멀티 변형 재생 목록의 정보만 사용하여 스트림을 준비합니다. 이는 #EXT-X-STREAM-INF 태그에 CODECS 속성이 포함된 경우에 작동합니다.
미디어 세그먼트에 #EXT-X-MEDIA:TYPE=CLOSED-CAPTIONS 태그가 있는 멀티 변형 재생목록에 선언되지 않은 멀티플렉싱된 자막 트랙이 포함된 경우 이 기능을 사용 중지해야 할 수 있습니다. 그렇지 않으면 이러한 자막 트랙이 감지되지 않고 재생되지 않습니다. 다음 스니펫과 같이 HlsMediaSource.Factory에서 청크 없는 준비를 사용 중지할 수 있습니다. ExoPlayer가 이러한 추가 트랙을 검색하기 위해 미디어 세그먼트를 다운로드해야 하므로 시작 시간이 늘어납니다. 대신 멀티 변형 재생목록에서 자막 트랙을 선언하는 것이 좋습니다.
Kotlin
val hlsMediaSource =
HlsMediaSource.Factory(dataSourceFactory)
.setAllowChunklessPreparation(false)
.createMediaSource(MediaItem.fromUri(hlsUri))
자바
HlsMediaSource hlsMediaSource =
new HlsMediaSource.Factory(dataSourceFactory)
.setAllowChunklessPreparation(false)
.createMediaSource(MediaItem.fromUri(hlsUri));
고품질 HLS 콘텐츠 만들기
ExoPlayer를 최대한 활용하려면 HLS 콘텐츠를 개선하기 위해 따를 수 있는 특정 가이드라인이 있습니다. 자세한 내용은 ExoPlayer의 HLS 재생에 관한 Medium 게시물을 참고하세요. 주요 사항은 다음과 같습니다.
- 정확한 세그먼트 지속 시간을 사용합니다.
- 연속 미디어 스트림을 사용하고 세그먼트 간에 미디어 구조를 변경하지 마세요.
#EXT-X-INDEPENDENT-SEGMENTS태그를 사용합니다.- 동영상과 오디오가 모두 포함된 파일이 아닌 디먹싱된 스트림을 선호합니다.
- 다양한 버전의 재생목록에 가능한 모든 정보를 포함합니다.
라이브 스트림에는 다음 가이드라인이 적용됩니다.
#EXT-X-PROGRAM-DATE-TIME태그를 사용합니다.#EXT-X-DISCONTINUITY-SEQUENCE태그를 사용합니다.- 긴 라이브 창을 제공합니다. 1분 이상이 좋습니다.