홀로렌즈 2는 웹에서 실시간 스트리밍을 지원하는 API를 제공
이 API를 활용하면 앱에서도 화면 미러링을 할 수 있음
본 글에서는 ExoPlayer를 이용해 미러링을 구현하는 방법을 소개
media3:1.3.1
라이브러리 버전 설정, 아래 코드는 libs.versions.toml 파일 기준준
media3 = "1.3.1"
media-exoplayer = { group = "androidx.media3", name = "media3-exoplayer", version.ref = "media3" }
media-exoplayer-dash = { group = "androidx.media3", name = "media3-exoplayer-dash", version.ref = "media3" }
media-ui = { group = "androidx.media3", name = "media3-ui", version.ref = "media3" }
dependencies {
implementation(libs.media.exoplayer)
implementation(libs.media.exoplayer.dash)
implementation(libs.media.ui)
}
스트리밍 API는 HTTPS로 제공되는데, 인증서를 신뢰하지 못하면 오류가 발생
이를 우회하려면 인증서를 강제로 신뢰하도록 설정해야 함
class TestApplication : Application() {
override fun onCreate() {
super.onCreate()
disableSSLCertificateChecking()
}
private fun disableSSLCertificateChecking() {
val trustAllCerts: Array<TrustManager> = arrayOf(
object : X509TrustManager {
override fun checkClientTrusted(chain: Array<out X509Certificate>?, authType: String?) {}
override fun checkServerTrusted(chain: Array<out X509Certificate>?, authType: String?) {}
override fun getAcceptedIssuers(): Array<X509Certificate> = arrayOf()
}
)
try {
val sc = SSLContext.getInstance("TLS")
sc.init(null, trustAllCerts, SecureRandom())
// 반드시 설정
HttpsURLConnection.setDefaultSSLSocketFactory(sc.socketFactory)
// 선택 사항
HttpsURLConnection.setDefaultHostnameVerifier { _, _ -> true }
} catch (e: Exception) {
e.printStackTrace()
}
}
}
Manifest에 Application 추가
<application
android:name=".TestApplication"
... />
홀로렌즈 API는 인증이 필요하며, 아이디와 비밀번호를 Base64로 인코딩한 값을 Authorization 헤더에 포함시켜야 함
"Authorization": "Basic Zm9ybWFsd29ya3M6Zm9ybWFsd29ya3M="
"아이디:비밀번호"
문자열을 Base64로 인코딩@OptIn(UnstableApi::class)
private fun getSourceFactory(): DataSource.Factory {
val defaultFactory = DefaultHttpDataSource.Factory()
.setUserAgent(Util.getUserAgent(this, getString(R.string.app_name)))
val headers = mapOf(
"Authorization" to "Basic Zm9ybWFsd29ya3M6Zm9ybWFsd29ya3M="
)
return ResolvingDataSource.Factory(defaultFactory) { spec ->
spec.withAdditionalHeaders(headers)
}
}
@OptIn(UnstableApi::class)
private fun getExoPlayer(): ExoPlayer {
val mediaItem = MediaItem.Builder().setUri(streamUri).build()
val mediaSource = ProgressiveMediaSource.Factory(getSourceFactory())
.createMediaSource(mediaItem)
return ExoPlayer.Builder(this)
.setLoadControl(DefaultLoadControl())
.setTrackSelector(DefaultTrackSelector(this))
.build().apply {
setMediaSource(mediaSource)
playWhenReady = true
repeatMode = Player.REPEAT_MODE_OFF
}
}
private val streamUri =
"https://~~~~~~~~~~/api/holographic/stream/live_low.mp4?holo=true&pv=false&mic=false&loopback=false&RenderFromCamera=true"
DataSource.Factory
커스터마이징