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.9.2")
Groovy
implementation "androidx.media3:media3-exoplayer-hls:1.9.2"
그런 다음 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, @Player.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
val 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 val player = ExoPlayer.Builder(context).setMediaSourceFactory(hlsMediaSourceFactory).build() // Set the player on the ads loader. hlsInterstitialsAdsLoader.setPlayer(player) playerView.setPlayer(player)
자바
HlsInterstitialsAdsLoader 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 ExoPlayer player = new ExoPlayer.Builder(context).setMediaSourceFactory(hlsMediaSourceFactory).build(); // Set the player on the ads loader. hlsInterstitialsAdsLoader.setPlayer(player); playerView.setPlayer(player);
이러한 플레이어 설정으로 HLS 전면 광고를 재생하려면 플레이어에서 AdsConfiguration가 있는 미디어 항목을 설정하면 됩니다.
Kotlin
// Build an HLS media item with ads configuration to be played. 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()
자바
// Build an HLS media item with ads configuration to be played. 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
val 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. val 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. ExoPlayer 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. player.setMediaSource(mediaSource); player.prepare(); player.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. } }
자바
@OptIn(markerClass = UnstableApi.class) private static 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
class HlsInterstitialsActivity : Activity() { 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?.player = null } }
자바
@OptIn(markerClass = UnstableApi.class) public static class HlsInterstitialsActivity extends Activity { 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. if (hlsInterstitialsAdsLoader != null) { 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); } } }
재생 맞춤설정
ExoPlayer는 앱의 요구사항에 맞게 재생 환경을 맞춤설정할 수 있는 여러 방법을 제공합니다. 예는 맞춤설정 페이지를 참고하세요.
청크 없는 준비 사용 중지
기본적으로 ExoPlayer는 청크 없는 준비를 사용합니다. 즉, ExoPlayer는 멀티 변형 재생 목록의 정보만 사용하여 스트림을 준비합니다. 이는 #EXT-X-STREAM-INF 태그에 CODECS 속성이 포함된 경우에 작동합니다.
미디어 세그먼트에 #EXT-X-MEDIA:TYPE=CLOSED-CAPTIONS 태그가 있는 멀티 변형 재생목록에 선언되지 않은 muxed 자막 트랙이 포함된 경우 이 기능을 사용 중지해야 할 수 있습니다. 그렇지 않으면 이러한 자막 트랙이 감지되지 않고 재생되지 않습니다. 다음 스니펫과 같이 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분 이상이면 좋습니다.