我正在 Android Studio 中开发 Kotlin 应用程序。我正在尝试使用 SoundCloud.com 的 API 和我的自定义 ExoPlayer 流式传输音频。我的问题来自尝试实际使用 ExoPlayer。
这里是 SoundCloud API Guide 及其 API Explorer 的链接,以供参考。我有一个 Client_ID,能够返回访问令牌,找到正确的曲目,并提取该曲目的流媒体 URL。我可以从轨道元数据中确认它是可公开访问的并且可从外部流式传输。流媒体 URL 采用以下格式:
"http_mp3_128_url": "https://cf-media.sndcdn.com/",
"hls_mp3_128_url": "https://cf-media.sndcdn.com/",
"hls_opus_64_url": "https://cf-media.sndcdn.com/",
"preview_mp3_128_url": "https://cf-media.sndcdn.co
使用“http_mp3_128_url”及其字符串的值,我可以将该字符串输入到我的网络浏览器中,该浏览器会打开一个页面并完美地传输音频。 *注意: 由于访问令牌过期,该 URL 只能持续大约一个小时。当收到此 ExoPlayer 错误时,我可以确认它仍然是一个“良好”链接。
每当我尝试使用该字符串创建 MediaSource 并将其放入我的 ExoPlayer 时,我都会收到源错误代码。据我从错误日志中可以看出,我的 ExoPlayer 正在尝试使用 FileDataSource 并返回
open failed: ENOENT (No such file or directory)
我尝试公开将 MediaSource 声明为 ProgressiveMediaSource 和 DefaultDataSource,并且我还尝试仅创建 MediaItem 并将其添加到播放器。运气不好,我收到的错误也没有改变。
ExoPlayer 设置:
private lateinit var player: ExoPlayer
private lateinit var playerView: PlayerView
private fun setupPlayer(){
player = ExoPlayer.Builder(this).build()
playerView = findViewById(R.id.gm_player)
playerView.player = player
player.addListener(this)
}
Logcat 输出:
ExoPlayerImplInternal Playback error
androidx.media3.exoplayer.ExoPlaybackException: Source error
at androidx.media3.exoplayer.ExoPlayerImplInternal.handleIoException(ExoPlayerImplInternal.java:684)
at androidx.media3.exoplayer.ExoPlayerImplInternal.handleMessage(ExoPlayerImplInternal.java:656)
at android.os.Handler.dispatchMessage(Handler.java:103)
at android.os.Looper.loop(Looper.java:214)
at android.os.HandlerThread.run(HandlerThread.java:67)
Caused by: androidx.media3.datasource.FileDataSource$FileDataSourceException: java.io.FileNotFoundException: : open failed: ENOENT (No such file or directory)
at androidx.media3.datasource.FileDataSource.openLocalFile(FileDataSource.java:205)
at androidx.media3.datasource.FileDataSource.open(FileDataSource.java:116)
at androidx.media3.datasource.DefaultDataSource.open(DefaultDataSource.java:272)
at androidx.media3.datasource.StatsDataSource.open(StatsDataSource.java:86)
at androidx.media3.exoplayer.source.ProgressiveMediaPeriod$ExtractingLoadable.load(ProgressiveMediaPeriod.java:1006)
at androidx.media3.exoplayer.upstream.Loader$LoadTask.run(Loader.java:414)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at java.lang.Thread.run(Thread.java:919)
Caused by: java.io.FileNotFoundException: : open failed: ENOENT (No such file or directory)
at libcore.io.IoBridge.open(IoBridge.java:496)
at java.io.RandomAccessFile.<init>(RandomAccessFile.java:289)
at java.io.RandomAccessFile.<init>(RandomAccessFile.java:152)
at androidx.media3.datasource.FileDataSource.openLocalFile(FileDataSource.java:186)
at androidx.media3.datasource.FileDataSource.open(FileDataSource.java:116)
at androidx.media3.datasource.DefaultDataSource.open(DefaultDataSource.java:272)
at androidx.media3.datasource.StatsDataSource.open(StatsDataSource.java:86)
at androidx.media3.exoplayer.source.ProgressiveMediaPeriod$ExtractingLoadable.load(ProgressiveMediaPeriod.java:1006)
at androidx.media3.exoplayer.upstream.Loader$LoadTask.run(Loader.java:414)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at java.lang.Thread.run(Thread.java:919)
Caused by: android.system.ErrnoException: open failed: ENOENT (No such file or directory)
at libcore.io.Linux.open(Native Method)
at libcore.io.ForwardingOs.open(ForwardingOs.java:167)
at libcore.io.BlockGuardOs.open(BlockGuardOs.java:252)
at libcore.io.ForwardingOs.open(ForwardingOs.java:167)
at android.app.ActivityThread$AndroidOs.open(ActivityThread.java:7255)
at libcore.io.IoBridge.open(IoBridge.java:482)
at java.io.RandomAccessFile.<init>(RandomAccessFile.java:289)
at java.io.RandomAccessFile.<init>(RandomAccessFile.java:152)
at androidx.media3.datasource.FileDataSource.openLocalFile(FileDataSource.java:186)
at androidx.media3.datasource.FileDataSource.open(FileDataSource.java:116)
at androidx.media3.datasource.DefaultDataSource.open(DefaultDataSource.java:272)
at androidx.media3.datasource.StatsDataSource.open(StatsDataSource.java:86)
at androidx.media3.exoplayer.source.ProgressiveMediaPeriod$ExtractingLoadable.load(ProgressiveMediaPeriod.java:1006)
at androidx.media3.exoplayer.upstream.Loader$LoadTask.run(Loader.java:414)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at java.lang.Thread.run(Thread.java:919)
有没有人有 ExoPlayer 或媒体源/流媒体的经验,可以帮助我了解发生了什么以及如何解决它?
尝试:使用流媒体 url“http_mp3_128_url”创建并分配 ProgressiveMediaSource:
val dataSource = DefaultHttpDataSource.Factory()
val medSource : MediaSource = ProgressiveMediaSource.Factory(dataSource)
.createMediaSource(fromUri(streamUrl))
player.setMediaSource(medSource)
player.prepare()
player.playWhenReady
player.play()
尝试:将 HlsMediaSource 与“http_mp3_128_url”、“hls_mp3_128_url”和“hls_opus_64_url”一起使用:
val dataSource = DefaultHttpDataSource.Factory()
val medSource : MediaSource = HlsMediaSource.Factory(dataSource)
.createMediaSource(fromUri(streamUrl))
player.setMediaSource(medSource)
尝试:直接创建并分配 MediaItem:
var medItem = MediaItem.Builder()
.setUri(streamUrl)
.setMimeType(MimeTypes.AUDIO_MPEG)
.build()
player.setMediaItem(medItem)
player.prepare()
player.playWhenReady
player.play()
在AndroidManifest中声明:
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<application
android:usesCleartextTraffic="true"
android:requestLegacyExternalStorage="true"/>
谢谢!
更新 - 问题已解决!
作为警告,我认为我有多个问题导致了我最初的问题,包括:
这是我的最终代码。
来自 main():
runBlocking {
launch {
delay(2000)
streamMusic()
}
getStreamingTrack()
}
这是我检索流媒体网址的地方。这是返回原帖中提到的 4 种 url 格式的方法。
private fun getStreamingTrack() {
GlobalScope.launch(Dispatchers.IO){
val trackUrl = URL("https://api.soundcloud.com/tracks/${trackId}/streams")
var trackConn = trackUrl.openConnection() as HttpsURLConnection
trackConn.requestMethod = "GET"
trackConn.setRequestProperty("accept", "application/json; charset=utf-8")
trackConn.setRequestProperty("Authorization", "OAuth $accessToken")
trackConn.doInput = true
trackConn.doOutput = false
// Check if the connection is successful
val responseCode = trackConn.responseCode
if (responseCode == HttpURLConnection.HTTP_OK) {
val response = trackConn.inputStream.bufferedReader().readText()
val jsonObject = JSONTokener(response).nextValue() as JSONObject
httpStreamUrl = jsonObject.getString("http_mp3_128_url")
// Uncomment line below if need to print parsed string to the log
//Log.i("STREAM URL : ", myUrl)
// Shouldn't need these, but these are the 3 other urls returned from SoundCloud
/*myHls1 = jsonObject.getString("hls_mp3_128_url")
val myHls2 = jsonObject.getString("hls_opus_64_url")
val myPrev = jsonObject.getString("preview_mp3_128_url")*/
trackConn.disconnect()
} else {
Log.e("Track Streaming Error", responseCode.toString())
Log.e("ERROR MESSAGE : ", trackConn.responseMessage)
}
}
}
此方法声明 ProgressiveMediaSource
private fun streamMusic() {
val dataSourceFactory = DefaultHttpDataSource.Factory()
.setAllowCrossProtocolRedirects(true)
val progressiveMediaSource = ProgressiveMediaSource.Factory(dataSourceFactory)
.createMediaSource(fromUri(httpStreamUrl))
player.setMediaSource(progressiveMediaSource)
player.prepare()
player.play()
}
希望这可以帮助其他可能遇到麻烦的人!