문제 해결


'일반 텍스트 HTTP 트래픽이 허용되지 않음' 오류 해결

앱의 네트워크 보안 구성에서 허용하지 않는 경우 앱이 일반 텍스트 HTTP 트래픽 (https://가 아닌 http://)을 요청하면 이 오류가 발생합니다. 앱이 Android 9 (API 수준 28) 이상을 타겟팅하는 경우 일반 텍스트 HTTP 트래픽은 기본 구성에서 사용 중지됩니다.

앱이 일반 텍스트 HTTP 트래픽으로 작동해야 하는 경우 이를 허용하는 네트워크 보안 구성을 사용해야 합니다. 자세한 내용은 Android's 네트워크 보안 문서 를 참고하세요. 모든 일반 텍스트 HTTP 트래픽을 사용 설정하려면 앱의 AndroidManifest.xml application 요소에 android:usesCleartextTraffic="true"를 추가하면 됩니다.

ExoPlayer 데모 앱은 기본 네트워크 보안 구성을 사용하므로 일반 텍스트 HTTP 트래픽을 허용하지 않습니다. 위의 안내에 따라 사용 설정할 수 있습니다.

'SSLHandshakeException', 'CertPathValidatorException', 'ERR_CERT_AUTHORITY_INVALID' 오류 해결

SSLHandshakeException, CertPathValidatorException, ERR_CERT_AUTHORITY_INVALID는 모두 서버의 SSL 인증서에 문제가 있음을 나타냅니다. 이러한 오류는 ExoPlayer와 관련이 없습니다. 자세한 내용은 Android의 SSL 문서 를 참고하세요.

일부 미디어 파일을 탐색할 수 없는 이유는 무엇인가요?

기본적으로 ExoPlayer는 정확한 탐색 작업을 실행하는 유일한 방법이 플레이어가 전체 파일을 스캔하고 색인을 생성하는 것인 미디어에서 탐색을 지원하지 않습니다. ExoPlayer는 이러한 파일을 탐색할 수 없는 것으로 간주합니다. 대부분의 최신 미디어 컨테이너 형식에는 탐색을 위한 메타데이터 (예: 샘플 색인)가 포함되어 있거나, 잘 정의된 탐색 알고리즘 (예: Ogg의 보간된 이분 검색)이 있거나, 콘텐츠가 고정 비트 전송률임을 나타냅니다. 이러한 경우 ExoPlayer에서 효율적인 탐색 작업을 실행하고 지원할 수 있습니다.

탐색이 필요하지만 탐색할 수 없는 미디어가 있는 경우 콘텐츠를 변환하여 더 적절한 컨테이너 형식을 사용하는 것이 좋습니다. MP3, ADTS, AMR 파일의 경우 여기에 설명된 대로 파일에 고정 비트 전송률이 있다고 가정하고 탐색을 사용 설정할 수도 있습니다. 여기

일부 MP3 파일에서 탐색이 부정확한 이유는 무엇인가요?

가변 비트 전송률 (VBR) MP3 파일은 정확한 탐색이 필요한 사용 사례에 근본적으로 적합하지 않습니다. 이러한 이유는 다음과 같습니다.

  1. 정확한 탐색을 위해 컨테이너 형식은 헤더에 정확한 시간-바이트 매핑을 제공하는 것이 좋습니다. 이 매핑을 사용하면 플레이어가 요청된 탐색 시간을 상응하는 바이트 오프셋에 매핑하고 해당 오프셋에서 미디어 요청, 파싱, 재생을 시작할 수 있습니다. MP3에서 이 매핑을 지정하는 데 사용할 수 있는 헤더 (예: XING 헤더)는 아쉽게도 종종 부정확합니다.
  2. 정확한 시간-바이트 매핑 (또는 시간-바이트 매핑)을 제공하지 않는 컨테이너 형식의 경우에도 컨테이너에 스트림의 절대 샘플 타임스탬프가 포함되어 있으면 정확한 탐색을 실행할 수 있습니다. 이 경우 플레이어는 탐색 시간을 상응하는 바이트 오프셋의 최적 추측에 매핑하고, 해당 오프셋에서 미디어 요청을 시작하고, 첫 번째 절대 샘플 타임스탬프를 파싱하고, 올바른 샘플을 찾을 때까지 미디어에 효과적으로 안내된 이분 검색을 실행할 수 있습니다. 아쉽게도 MP3에는 스트림에 절대 샘플 타임스탬프가 포함되어 있지 않으므로 이 접근 방식은 불가능합니다.

이러한 이유로 VBR MP3 파일에서 정확한 탐색을 실행하는 유일한 방법은 전체 파일을 스캔하고 플레이어에서 시간-바이트 매핑을 수동으로 빌드하는 것입니다. 이 전략은 FLAG_ENABLE_INDEX_SEEKING를 사용하여 사용 설정할 수 있으며, DefaultExtractorsFactory를 사용하여 setMp3ExtractorFlags에서 설정할 수 있습니다. 특히 사용자가 재생을 시작한 직후 스트림의 끝 근처로 탐색하려고 하면 큰 MP3 파일로 잘 확장되지 않습니다. 이 경우 플레이어가 탐색을 실행하기 전에 전체 스트림을 다운로드하고 색인을 생성할 때까지 기다려야 합니다. ExoPlayer에서는 이 경우 정확성보다 속도를 최적화하기로 결정했으므로 FLAG_ENABLE_INDEX_SEEKING은 기본적으로 사용 중지됩니다.

재생 중인 미디어를 제어하는 경우 MP4와 같은 더 적절한 컨테이너 형식을 사용하는 것이 좋습니다. MP3가 미디어 형식으로 가장 적합한 사용 사례는 없습니다.

동영상에서 탐색이 느린 이유는 무엇인가요?

플레이어가 동영상에서 새 재생 위치를 탐색할 때는 다음 두 가지 작업을 실행해야 합니다.

  1. 새 재생 위치에 해당하는 데이터를 버퍼에 로드합니다(이 데이터가 이미 버퍼링된 경우 필요하지 않을 수 있음).
  2. 대부분의 동영상 압축 형식에서 사용하는 프레임 내 코딩으로 인해 동영상 디코더를 플러시하고 새 재생 위치 앞의 I-프레임 (키프레임)에서 디코딩을 시작합니다. 탐색이 정확하도록 (즉, 재생이 탐색 위치에서 정확히 시작되도록) 이전 I-프레임과 탐색 위치 사이의 모든 프레임을 디코딩하고 즉시 삭제해야 합니다 (화면에 표시하지 않고).

(1)로 인해 발생하는 지연 시간은 플레이어에서 메모리에 버퍼링되는 데이터 양을 늘리거나 데이터를 디스크에 사전 캐싱하여 완화할 수 있습니다.

(2)로 인해 발생하는 지연 시간은 탐색의 정확성을 줄이거나 동영상을 다시 인코딩하여 I-프레임을 더 자주 포함하도록 하여 완화할 수 있습니다 (출력 파일이 더 커짐).ExoPlayer.setSeekParameters

일부 MPEG-TS 파일을 재생할 수 없는 이유는 무엇인가요?

일부 MPEG-TS 파일에는 액세스 단위 구분 기호 (AUD)가 포함되어 있지 않습니다. 기본적으로 ExoPlayer는 AUD를 사용하여 프레임 경계를 저렴하게 감지합니다. 마찬가지로 일부 MPEG-TS 파일에는 IDR 키프레임이 포함되어 있지 않습니다. 기본적으로 ExoPlayer에서 고려하는 유일한 키프레임 유형입니다.

ExoPlayer는 AUD 또는 IDR 키프레임이 없는 MPEG-TS 파일을 재생하도록 요청받으면 버퍼링 상태에서 멈춘 것처럼 보입니다. 이러한 파일을 재생해야 하는 경우, FLAG_DETECT_ACCESS_UNITSFLAG_ALLOW_NON_IDR_KEYFRAMES를 사용하여 재생할 수 있습니다. 이러한 플래그는 다음을 사용하여 DefaultExtractorsFactory 설정하거나 DefaultHlsExtractorFactory 다음을 사용하여 생성자에서 설정할 수 있습니다.setTsExtractorFlags FLAG_DETECT_ACCESS_UNITS를 사용하면 AUD 기반 프레임 경계 감지에 비해 계산 비용이 많이 든다는 점을 제외하고는 부작용이 없습니다. FLAG_ALLOW_NON_IDR_KEYFRAMES를 사용하면 일부 MPEG-TS 파일을 재생할 때 재생 시작 시와 탐색 직후에 일시적인 시각적 손상이 발생할 수 있습니다.

일부 MPEG-TS 파일에서 자막을 찾을 수 없는 이유는 무엇인가요?

일부 MPEG-TS 파일에는 CEA-608 트랙이 포함되어 있지만 컨테이너 메타데이터에서 선언하지 않으므로 ExoPlayer에서 이를 감지할 수 없습니다. MPEG-TS 스트림에서 자막 트랙을 식별하는 데 사용할 수 있는 접근성 채널을 포함하여 예상되는 자막 형식 목록을 DefaultExtractorsFactory에 제공하여 자막 트랙을 수동으로 지정할 수 있습니다.

Kotlin

val extractorsFactory =
  DefaultExtractorsFactory()
    .setTsSubtitleFormats(
      listOf(
        Format.Builder()
          .setSampleMimeType(MimeTypes.APPLICATION_CEA608)
          .setAccessibilityChannel(accessibilityChannel)
          // Set other subtitle format info, such as language.
          .build()
      )
    )
val player: Player =
  ExoPlayer.Builder(context, DefaultMediaSourceFactory(context, extractorsFactory)).build()

자바

DefaultExtractorsFactory extractorsFactory =
    new DefaultExtractorsFactory()
        .setTsSubtitleFormats(
            ImmutableList.of(
                new Format.Builder()
                    .setSampleMimeType(MimeTypes.APPLICATION_CEA608)
                    .setAccessibilityChannel(accessibilityChannel)
                    // Set other subtitle format info, such as language.
                    .build()));
Player player =
    new ExoPlayer.Builder(context, new DefaultMediaSourceFactory(context, extractorsFactory))
        .build();

일부 MP4/FMP4 파일이 잘못 재생되는 이유는 무엇인가요?

일부 MP4/FMP4 파일에는 샘플 목록을 건너뛰거나 이동하거나 반복하여 미디어 타임라인을 다시 작성하는 수정 목록이 포함되어 있습니다. ExoPlayer는 수정 목록 적용을 부분적으로 지원합니다. 예를 들어 동기화 샘플에서 시작하는 샘플 그룹을 지연하거나 반복할 수 있지만 동기화 샘플에서 시작하지 않는 수정의 경우 오디오 샘플을 자르거나 미디어를 미리 롤링하지 않습니다.

미디어의 일부가 예기치 않게 누락되거나 반복되는 경우 Mp4Extractor.FLAG_WORKAROUND_IGNORE_EDIT_LISTS 또는 FragmentedMp4Extractor.FLAG_WORKAROUND_IGNORE_EDIT_LISTS를 설정해 보세요. 이렇게 하면 추출기가 수정 목록을 완전히 무시합니다. 이러한 플래그는 다음을 사용하여 DefaultExtractorsFactory setMp4ExtractorFlags 또는 setFragmentedMp4ExtractorFlags에 설정할 수 있습니다.

일부 스트림이 HTTP 응답 코드 301 또는 302로 실패하는 이유는 무엇인가요?

HTTP 응답 코드 301과 302는 모두 리디렉션을 나타냅니다. 간략한 설명은 Wikipedia에서 확인할 수 있습니다. ExoPlayer가 요청을 하고 상태 코드 301 또는 302로 응답을 받으면 일반적으로 리디렉션을 따르고 정상적으로 재생을 시작합니다. 기본적으로 이 작업이 실행되지 않는 한 가지 경우는 교차 프로토콜 리디렉션입니다. 교차 프로토콜 리디렉션은 HTTPS에서 HTTP로 또는 그 반대로 리디렉션하는 리디렉션입니다 (또는 덜 일반적이지만 다른 프로토콜 쌍 간에 리디렉션). 다음과 같이 wget 명령줄 도구를 사용하여 URL이 교차 프로토콜 리디렉션을 일으키는지 테스트할 수 있습니다.

wget "https://yourserver.example.com/test.mp3" 2>&1  | grep Location

출력은 다음과 같이 표시됩니다.

Location: https://secondserver.example.net/test.mp3 [following]
Location: http://thirdserver.example.org/test.mp3 [following]

이 예에서는 두 개의 리디렉션이 있습니다. 첫 번째 리디렉션은 https://yourserver.example.com/test.mp3에서 https://secondserver.example.net/test.mp3로의 리디렉션입니다. 둘 다 HTTPS이므로 교차 프로토콜 리디렉션이 아닙니다. 두 번째 리디렉션은 https://secondserver.example.net/test.mp3에서 http://thirdserver.example.org/test.mp3로의 리디렉션입니다. 이 리디렉션은 HTTPS에서 HTTP로 리디렉션하므로 교차 프로토콜 리디렉션입니다. ExoPlayer는 기본 구성에서 이 리디렉션을 따르지 않으므로 재생이 실패합니다.

필요한 경우 애플리케이션에서 사용되는 DefaultHttpDataSource.Factory 인스턴스를 인스턴스화할 때 교차 프로토콜 리디렉션을 따르도록 ExoPlayer를 구성할 수 있습니다. 여기에서 네트워크 스택 선택 및 구성에 관해 알아보세요 .

일부 스트림이 UnrecognizedInputFormatException으로 실패하는 이유는 무엇인가요?

이 질문은 다음과 같은 형식의 재생 실패와 관련이 있습니다.

UnrecognizedInputFormatException: None of the available extractors
(MatroskaExtractor, FragmentedMp4Extractor, ...) could read the stream.

이 실패에는 두 가지 가능한 원인이 있습니다. 가장 일반적인 원인은 DASH (mpd), HLS (m3u8) 또는 SmoothStreaming (ism, isml) 콘텐츠를 재생하려고 하지만 플레이어가 프로그레시브 스트림으로 재생하려고 하는 것입니다. 이러한 스트림을 재생하려면 각 ExoPlayer 모듈에 종속되어야 합니다. 스트림 URI가 표준 파일 확장자로 끝나지 않는 경우 MediaItem.BuildersetMimeTypeMimeTypes.APPLICATION_MPD, MimeTypes.APPLICATION_M3U8 또는 MimeTypes.APPLICATION_SS를 전달하여 스트림 유형을 명시적으로 지정할 수도 있습니다.

두 번째로 덜 일반적인 원인은 ExoPlayer가 재생하려는 미디어의 컨테이너 형식을 지원하지 않는 것입니다. 이 경우 실패는 의도한 대로 작동하지만 컨테이너 형식과 테스트 스트림의 세부정보를 포함하여 Issue Tracker에 기능 요청을 제출해 주세요. 새 기능 요청을 제출하기 전에 기존 기능 요청을 검색하세요.

일부 기기에서 setPlaybackParameters가 제대로 작동하지 않는 이유는 무엇인가요?

Android M 이하에서 앱의 디버그 빌드를 실행할 때 끊김 현상, 들리는 아티팩트, 높은 CPU 사용률이 발생할 수 있습니다.setPlaybackParameters 이는 이 API에 중요한 최적화가 이러한 버전의 Android에서 실행되는 디버그 빌드에서 사용 중지되기 때문입니다.

이 문제는 디버그 빌드에만 영향을 미친다는 점에 유의해야 합니다. 최적화가 항상 사용 설정되는 출시 빌드에는 영향을 미치지 않습니다. 따라서 최종 사용자에게 제공하는 출시에는 이 문제가 영향을 미치지 않아야 합니다.

'플레이어가 잘못된 스레드에서 액세스됨' 오류는 무엇을 의미하나요?

시작하기 페이지의 스레딩 관련 참고사항을 참고하세요.

'예상치 못한 상태 줄: ICY 200 OK'를 해결하려면 어떻게 해야 하나요?

이 문제는 서버 응답에 HTTP 호환 상태 줄이 아닌 ICY 상태 줄이 포함되어 있는 경우 발생할 수 있습니다. ICY 상태 줄은 지원 중단되었으며 사용해서는 안 되므로 서버를 제어하는 경우 HTTP 호환 응답을 제공하도록 업데이트해야 합니다. 이렇게 할 수 없는 경우 ExoPlayer OkHttp 라이브러리를 사용하면 ICY 상태 줄을 올바르게 처리할 수 있으므로 문제가 해결됩니다.

재생 중인 스트림이 라이브 스트림인지 어떻게 쿼리할 수 있나요?

플레이어의 isCurrentWindowLive 메서드를 쿼리할 수 있습니다. 또한 you can check isCurrentWindowDynamic을 확인하여 창이 동적인지 (즉, 시간이 지남에 따라 계속 업데이트되는지) 확인할 수 있습니다.

앱이 백그라운드에 있을 때 오디오를 계속 재생하려면 어떻게 해야 하나요?

앱이 백그라운드에 있을 때 오디오가 계속 재생되도록 하려면 다음 단계를 따르세요.

  1. 포그라운드 서비스가 실행 중이어야 합니다. 이렇게 하면 시스템이 리소스를 확보하기 위해 프로세스를 종료하지 않습니다.
  2. WifiLockWakeLock을 보유해야 합니다. 이렇게 하면 시스템이 Wi-Fi 라디오와 CPU를 절전 모드에서 해제합니다. ExoPlayer를 사용하는 경우 setWakeMode를 호출하여 이를 쉽게 실행할 수 있습니다. 그러면 필요한 잠금이 올바른 시간에 자동으로 획득되고 해제됩니다.

오디오가 더 이상 재생되지 않으면 잠금을 해제하고 (setWakeMode를 사용하지 않는 경우) 서비스를 중지하는 것이 중요합니다.

ExoPlayer는 내 콘텐츠를 지원하지만 ExoPlayer Cast 라이브러리는 지원하지 않는 이유는 무엇인가요?

재생하려는 콘텐츠가 CORS 사용 설정되지 않았을 수 있습니다. Cast 프레임워크는 콘텐츠를 재생하려면 CORS를 사용 설정해야 합니다.

콘텐츠가 재생되지 않지만 오류가 표시되지 않는 이유는 무엇인가요?

콘텐츠를 재생하는 기기가 특정 미디어 샘플 형식을 지원하지 않을 수 있습니다. 플레이어에 EventLogger를 리스너로 추가하고 Logcat에서 다음과 유사한 줄을 찾아보면 쉽게 확인할 수 있습니다.

[ ] Track:x, id=x, mimeType=mime/type, ... , supported=NO_UNSUPPORTED_TYPE

NO_UNSUPPORTED_TYPE 은 기기가 mimeType으로 지정된 미디어 샘플 형식을 디코딩할 수 없음을 의미합니다. 지원되는 샘플 형식에 관한 자세한 내용은 Android 미디어 형식 문서를 참고하세요. 디코딩 라이브러리를 로드하여 재생에 사용하려면 어떻게 해야 하나요?도 유용할 수 있습니다.

디코딩 라이브러리를 로드하여 재생에 사용하려면 어떻게 해야 하나요?

디코딩 라이브러리 사용에 계속 문제가 발생하는 경우 Media3 문제 추적기에서 관련 최근 문제를 확인하세요. 새 문제를 제출해야 하고 라이브러리의 네이티브 부분을 빌드하는 것과 관련된 경우 문제를 진단하는 데 도움이 되도록 README 안내 실행의 전체 명령줄 출력을 포함해 주세요.

ExoPlayer로 YouTube 동영상을 직접 재생할 수 있나요?

아니요. ExoPlayer는 https://www.youtube.com/watch?v=... 형식의 URL과 같은 YouTube 동영상을 재생할 수 없습니다. 대신 Android에서 YouTube 동영상을 재생하는 공식 방법인 YouTube IFrame Player API를 사용해야 합니다.

동영상 재생이 끊김 현상

예를 들어 콘텐츠 비트 전송률 또는 해상도가 기기 기능을 초과하는 경우 기기가 콘텐츠를 충분히 빠르게 디코딩하지 못할 수 있습니다. 이러한 기기에서 좋은 성능을 얻으려면 낮은 품질의 콘텐츠를 사용해야 할 수 있습니다.

Android 6.0 (API 수준 23)부터 Android 11 (API 수준 30)까지의 Android 버전을 실행하는 기기에서 동영상 끊김 현상이 발생하는 경우, 특히 DRM으로 보호된 콘텐츠 또는 높은 프레임 속도 콘텐츠를 재생할 때 비동기 버퍼 큐를 사용 설정해 보세요.

불안정한 API 린트 오류

Media3는 API 표시 경로의 하위 집합에 대해 바이너리 호환성을 보장합니다. 바이너리 호환성을 보장하지 않는 부분은 @UnstableApi로 표시됩니다. 이러한 구분을 명확하게 하기 위해 불안정한 API 기호의 사용은 @OptIn으로 주석 처리되지 않는 한 린트 오류를 생성합니다.

@UnstableApi 주석은 API의 품질 또는 성능에 관해 아무것도 의미하지 않으며 'API 고정'이 아니라는 사실만 의미합니다.

불안정한 API 린트 오류를 처리하는 방법에는 두 가지가 있습니다.

  • 동일한 결과를 얻는 안정적인 API를 사용하도록 전환합니다.
  • 불안정한 API를 계속 사용하고 나중에 표시된 대로 @OptIn으로 사용에 주석을 추가합니다.
@OptIn 주석 추가

Android 스튜디오를 사용하면 주석을 추가할 수 있습니다.

스크린샷: 선택 주석을 추가하는 방법
그림 2: Android 스튜디오로 @androidx.annotations.OptIn 주석 추가

특정 사용 사이트에 수동으로 주석을 추가할 수도 있습니다.

Kotlin

import androidx.annotation.OptIn
import androidx.media3.common.util.UnstableApi

@OptIn(UnstableApi::class)
fun functionUsingUnstableApi() { ... }

자바

import androidx.annotation.OptIn;
import androidx.media3.common.util.UnstableApi;

@OptIn(markerClass = UnstableApi.class)
private void methodUsingUnstableApis() { ... }

package-info 파일을 추가하여 전체 패키지를 선택할 수 있습니다.

Kotlin

// In your package-info.kt
@OptIn(UnstableApi::class)
package name.of.your.package

import androidx.annotation.OptIn
import androidx.media3.common.util.UnstableApi

자바

// In your package-info.java
@OptIn(markerClass = UnstableApi.class)
package name.of.your.package;

import androidx.annotation.OptIn;
import androidx.media3.common.util.UnstableApi;

` lint.xml 파일`에서 특정 린트 오류를 표시하지 않음으로써 전체 프로젝트를 선택할 수 있습니다.

 <?xml version="1.0" encoding="utf-8"?>
 <lint>
   <issue id="UnsafeOptInUsageError">
     <option name="opt-in" value="androidx.media3.common.util.UnstableApi" />
   </issue>
 </lint>

사용해서는 안 되는 kotlin.OptIn 주석도 있습니다. androidx.annotation.OptIn 주석을 사용하는 것이 중요합니다.