安卓汽车 : Media item gets stuck "Getting your selection..." instead of showing controls while playing

标签 android exoplayer android-auto android-mediasession mediabrowserservicecompat

我有一个实现 MediaBrowserServiceCompat()PlaybackService.kt 类,因为它是 Android Auto 支持所必需的。播放器本身在应用程序内运行良好。该应用 95% 用于几个不同 channel 之间的 HLS 流(5% 是音频样本文件)。

我可以成功获取我的 Root View ,它是一个简单的 channel 列表(“直播”、“80 年代”、“90 年代”等),当我单击其中一个时,音频开始播放,但我卡在了“正在获取您的选择...”屏幕,如下所示。查看logcat,没有报错。有一点可能很奇怪,也可能很正常,onGetRoot 在选择 channel 后再次调用。

Android Auto error

这是我的整个 PlaybackService.kt 文件。

package <my-package>.audio.service

import android.app.Notification
import android.app.PendingIntent
import android.content.Intent
import android.content.res.Configuration
import android.net.Uri
import android.os.Binder
import android.os.Bundle
import android.os.IBinder
import android.os.ResultReceiver
import android.support.v4.media.MediaBrowserCompat
import android.support.v4.media.MediaDescriptionCompat
import android.support.v4.media.MediaMetadataCompat
import android.support.v4.media.session.MediaSessionCompat
import android.support.v4.media.session.PlaybackStateCompat
import android.util.Log
import androidx.annotation.MainThread
import androidx.core.content.ContextCompat
import androidx.media.MediaBrowserServiceCompat
import androidx.media.session.MediaButtonReceiver
import <my-package>.MainActivity
import <my-package>.R
import <my-package>.audio.api.ChannelApi
import <my-package>.audio.api.StreamApi
import <my-package>.audio.models.NetworkChannel
import <my-package>.audio.models.State
import <my-package>.audio.utils.StorageHelper
import com.facebook.react.bridge.Arguments
import com.facebook.react.bridge.ReactContext
import com.facebook.react.bridge.WritableMap
import com.facebook.react.modules.core.DeviceEventManagerModule
import com.google.android.exoplayer2.*
import com.google.android.exoplayer2.audio.AudioAttributes
import com.google.android.exoplayer2.ext.mediasession.MediaSessionConnector
import com.google.android.exoplayer2.metadata.id3.TextInformationFrame
import com.google.android.exoplayer2.metadata.id3.UrlLinkFrame
import com.google.android.exoplayer2.ui.PlayerNotificationManager
import com.google.android.exoplayer2.util.MimeTypes
import com.npaw.youbora.lib6.exoplayer2.Exoplayer2Adapter
import com.npaw.youbora.lib6.plugin.Options
import com.npaw.youbora.lib6.plugin.Plugin
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.cancel
import kotlinx.coroutines.launch
import java.util.*

open class PlaybackService : MediaBrowserServiceCompat() {
    var reactContext: ReactContext? = null
    private val binder = PlaybackBinder()
    private val scope = MainScope()
    private var sourceUrl: String? = null

    private var isForegroundService = false

    private val playerListener = playerListener()

    private val playbackAudioAttributes = AudioAttributes.Builder()
        .setContentType(C.AUDIO_CONTENT_TYPE_MUSIC)
        .setUsage(C.USAGE_MEDIA)
        .build()

    private val player: ExoPlayer by lazy {
        ExoPlayer.Builder(this).build().apply {
            setAudioAttributes(playbackAudioAttributes, true)
            setHandleAudioBecomingNoisy(true)
            setWakeMode(C.WAKE_MODE_NETWORK)
            addListener(playerListener)
            setPlaybackSpeed(1.0f)
            playWhenReady = true
        }
    }

    private val youboraOptions = Options()
    private lateinit var youboraPlugin: Plugin

    private lateinit var notificationManager: NotificationManager
    private lateinit var mediaSession: MediaSessionCompat
    private lateinit var mediaSessionConnector: MediaSessionConnector

    private var autoStations: List<NetworkChannel>? = null

    private lateinit var appName: String

    // START: Audio Module actions
    fun play(streamName: String){
        Log.d(TAG, "Processing stream: $streamName")
        youboraOptions.contentChannel = streamName
        buildStreamAndPlay(streamName)
    }

    fun playHook(hookUri: String, songTitle: String, artistName: String){
        Log.d(TAG, "Process hook file: $hookUri")
        youboraOptions.contentTitle = "$songTitle-$artistName"
        buildMediaAndPlay(hookUri)
    }

    fun stop(){
        Log.d(TAG, "Stopping stream.")
        player.stop()
        player.clearMediaItems()
    }

    fun getPlaybackState(): Int {
        return player.playbackState
    }
    // END: Audio Module actions

    override fun onCreate() {
        Log.d(TAG, "onCreate")
        super.onCreate()

        appName = applicationContext.getString(R.string.app_name)

        val sessionActivityPendingIntent = PendingIntent.getActivity(
            /* context = */ reactContext ?: applicationContext,
            /* requestCode = */ 0,
            /* intent = */ Intent(reactContext ?: applicationContext, MainActivity::class.java),
            /* flags = */ PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
        )

        mediaSession = MediaSessionCompat(this, TAG).apply {
            setSessionActivity(sessionActivityPendingIntent)
            isActive = true
        }

        sessionToken = mediaSession.sessionToken

        notificationManager = NotificationManager(
            this,
            mediaSession.sessionToken,
            PlayerNotificationListener()
        )

        mediaSessionConnector = MediaSessionConnector(mediaSession)
        mediaSessionConnector.setPlaybackPreparer(AudioServicePreparer())
        mediaSessionConnector.setPlayer(player)

        notificationManager.showNotificationForPlayer(player)
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        Log.d(TAG, "onStartCommand")
        MediaButtonReceiver.handleIntent(mediaSession, intent)
        return START_STICKY
    }

    override fun onBind(intent: Intent?): IBinder {
        if (intent != null) {
            if(SERVICE_INTERFACE == intent.action){
                return super.onBind(intent)!!
            }
        }
        return binder
    }

    override fun onTaskRemoved(rootIntent: Intent?) {
        super.onTaskRemoved(rootIntent)
        player.stop()
        player.clearMediaItems()
    }

    override fun onGetRoot(
        clientPackageName: String,
        clientUid: Int,
        rootHints: Bundle?
    ): BrowserRoot? {
        Log.d(TAG, "onGetRoot called")
        return BrowserRoot(MEDIA_ROOT_ID, null)
    }

    override fun onLoadChildren(
        parentId: String,
        result: Result<List<MediaBrowserCompat.MediaItem>>
    ) {
        Log.d(TAG, "OnLoadChildren: parentMediaId = $parentId")

        if(EMPTY_MEDIA_ROOT_ID == parentId){
            result.sendResult(null)
            return
        }

        val mediaItems: MutableList<MediaBrowserCompat.MediaItem> = mutableListOf()

        if(MEDIA_ROOT_ID == parentId){
            // Build list of channels
            Log.d(TAG, "Building root items")
            if (autoStations != null) {
                Log.d(TAG, "Channels exist, creating media items...")
                for (i in 0 until autoStations!!.size) {
                    val station = autoStations!![i]
                    val mediaItem = createMediaItem(station)
                    if (mediaItem != null) {
                        mediaItems.add(mediaItem)
                    }
                }
                result.sendResult(mediaItems)
            } else {
                Log.d(TAG, "Making call to get channels...")
                result.detach()
                scope.launch {
                    try {
                        autoStations = ChannelApi.retrofitService.getChannels()

                        if (autoStations!!.isNotEmpty()) {
                            Log.d(TAG, "Stations fetched, creating media items...")
                            for (station in autoStations!!) {
                                val mediaItem = createMediaItem(station)
                                if (mediaItem != null) {
                                    mediaItems.add(mediaItem)
                                }
                            }
                            result.sendResult(mediaItems)
                        }else{
                            useFallbackStation()
                        }
                    } catch (err: Exception) {
                        Log.e(TAG, "Failed to get channels from API. Reason: ${err.message}")
                        result.sendResult(mediaItems)
                    }
                }
            }
        } else{
            result.sendResult(mediaItems)
        }
    }

    private fun createMediaItem(station: NetworkChannel): MediaBrowserCompat.MediaItem? {
        return try {
            val description = MediaDescriptionCompat.Builder()
                .setMediaId(station.streamName)
                .setTitle(station.title)

            val isDarkMode = (reactContext ?: applicationContext).resources.configuration.uiMode == Configuration.UI_MODE_NIGHT_YES

            if (isDarkMode) {
                description.setIconUri(
                    Uri.parse("<my-cdn>" + station.streamName + "/album-md.png")
                )
            } else {
                description.setIconUri(
                    Uri.parse("<my-cdn>" + station.streamName + "/album-md-dark.png")
                )
            }

            MediaBrowserCompat.MediaItem(
                description.build(),
                MediaBrowserCompat.MediaItem.FLAG_PLAYABLE
            )
        } catch (ex: Exception) {
            null
        }
    }

    private fun useFallbackStation() {
        autoStations = List(1) {
            NetworkChannel(
                channelId = 5,
                title = appName,
                streamName = appName.lowercase(Locale.getDefault()),
                slug = "live",
                inactiveOn = null
            )
        }
    }

    override fun onDestroy() {
        scope.cancel()

        mediaSession.run {
            isActive = false
            release()
        }

        player.removeListener(playerListener)
        player.release()

        super.onDestroy()
    }

    private fun playStream(){
        Log.d(TAG, "playStream - sourceUrl: $sourceUrl")
        player.clearMediaItems()

        val mediaItem = MediaItem.Builder()
            .setUri(sourceUrl)
            .setMimeType(MimeTypes.APPLICATION_M3U8)
            .setLiveConfiguration(
                MediaItem.LiveConfiguration.Builder()
                    .setMaxPlaybackSpeed(1.02f)
                    .build()
            )
            .build()

        // Testing adding metadata for AA issue, no change...
        mediaSession.setMetadata(MediaMetadataCompat.Builder()
            .putString(MediaMetadataCompat.METADATA_KEY_TITLE, "Test 1")
            .putString(MediaMetadataCompat.METADATA_KEY_ARTIST, "Test 2")
            .putLong(MediaMetadataCompat.METADATA_KEY_DURATION, -1)
            .build())

        if (player.isPlaying || player.isLoading) {
            stop()
        }

        player.addMediaItem(mediaItem)
        player.prepare()
    }

    private fun buildStreamAndPlay(streamName: String) {
        scope.launch {
            sourceUrl = StreamApi.retrofitService.getStreamUrl(
                mapOf(
                    "encoding" to "hls",
                    "platform" to "android",
                    "network" to streamName,
                )
            ).data.streamUrl

            Log.d(TAG, "Got stream url: $sourceUrl")

            playStream()
        }
    }

    private fun buildMediaAndPlay(hookUri: String) = scope.launch {
        val userData = StorageHelper.getUserData(reactContext ?: applicationContext)

        if(!::youboraPlugin.isInitialized && reactContext != null){
            youboraPlugin = Plugin(youboraOptions, reactContext)
            youboraPlugin.activity = reactContext?.currentActivity

            Exoplayer2Adapter(player)
                .also { adapter ->
                    youboraPlugin.adapter = adapter
                }
        }

        if (player.isPlaying == true || player.isLoading == true) {
            stop()
        }

        val mediaItem = MediaItem.Builder()
            .setUri(hookUri)
            .build()

        player.addMediaItem(mediaItem)
        player.prepare()
        player.play()
    }

    @MainThread
    private fun playerListener() = object: Player.Listener {
        override fun onPlaybackStateChanged(playbackState: Int) {
            val state: State = when (playbackState){
                ExoPlayer.STATE_IDLE -> State.Idle
                ExoPlayer.STATE_BUFFERING -> State.Buffering
                ExoPlayer.STATE_READY -> State.Ready
                ExoPlayer.STATE_ENDED -> State.Stopped
                else -> State.Stopped
            }

            if(state != State.Buffering){
                notificationManager.hideNotification()
            }

            Log.d(TAG, "PlayerState: $state")
            sendEvent(EVENT_PLAYER_STATE, state.state )
        }

        override fun onIsPlayingChanged(isPlaying: Boolean) {
            if(isPlaying) {
                Log.d(TAG, "PlayerState: Playing")
                notificationManager.showNotificationForPlayer(player)
                sendEvent(EVENT_PLAYER_STATE, State.Playing.state)
            }else{
                Log.d(TAG, "PlayerState: Paused (Stopping and clearing)")
                sendEvent(EVENT_PLAYER_STATE, State.Paused.state)
                player.stop()
                player.clearMediaItems()
            }
        }

        override fun onPlayerError(error: PlaybackException) {
            sendEvent(EVENT_PLAYER_STATE, State.Error.state)
            super.onPlayerError(error)
        }

        override fun onMetadata(metadata: com.google.android.exoplayer2.metadata.Metadata) {
            var handled = false

            var id: String? = null
            var title: String? = null
            var artist: String? = null
            var isBreak = false

            Log.d(TAG, "Raw Meta: $metadata")

            (0 until metadata.length()).forEach { i ->
                when (val entry = metadata[i]) {
                    is TextInformationFrame -> {
                        when (entry.id.uppercase()) {
                            "TIT2", "TT2" -> {
                                if(entry.value.isNotEmpty()){ // Added to due current bug with empty metadata
                                    handled = true
                                    title = entry.value
                                }
                            }
                            "TOPE", "TPE1", "TP1" -> {
                                handled = true
                                artist = entry.value
                            }
                            "TUID" -> {
                                handled = true
                                id = entry.value
                            }
                        }
                    }
                    is UrlLinkFrame -> {
                        when (entry.id.uppercase()) {
                            "WXXX" -> {
                                handled = true
                                if (id == null) {
                                    id = entry.url
                                }
                            }
                        }
                    }
                }
            }

            if (handled){
                try {
                    UUID.fromString(id)
                } catch (ignored: Exception) {
                    Log.d(TAG, "Detected stream break.")
                    id = null
                    isBreak = true
                }

                notificationManager.currentArtist = artist.toString()
                notificationManager.currentSong = title.toString()

                youboraOptions.contentTitle = "$title-$artist"
                youboraOptions.contentId = "$id"

                youboraOptions.contentBitrate = player.audioFormat?.bitrate?.toLong()

                Arguments.createMap().apply {
                    putString("id", id)
                    putString("title", title)
                    putString("artist", artist)
                    putBoolean("isBreak", isBreak)
                    sendEvent(EVENT_METADATA, this)
                }
            }
        }
    }

    private fun sendEvent(eventName: String, params: WritableMap?){
        reactContext?.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
            ?.emit(eventName, params)
    }

    private fun sendEvent(eventName: String, eventValue: String){
        reactContext?.getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
            ?.emit(eventName, eventValue)
    }

    inner class PlaybackBinder: Binder(){
        val service = this@PlaybackService
    }

    /**
     * Listen for notification events.
     */
    private inner class PlayerNotificationListener :
        PlayerNotificationManager.NotificationListener {
        override fun onNotificationPosted(
            notificationId: Int,
            notification: Notification,
            ongoing: Boolean
        ) {
            if (ongoing && !isForegroundService) {
                ContextCompat.startForegroundService(
                    applicationContext,
                    Intent(applicationContext, this@PlaybackService.javaClass)
                )

                startForeground(notificationId, notification)
                isForegroundService = true
            }
        }

        override fun onNotificationCancelled(notificationId: Int, dismissedByUser: Boolean) {
            stopForeground(true)
            isForegroundService = false
            stopSelf()
        }
    }

    private inner class AudioServicePreparer: MediaSessionConnector.PlaybackPreparer {
        override fun onCommand(
            player: Player,
            command: String,
            extras: Bundle?,
            cb: ResultReceiver?
        ): Boolean  = false

        override fun getSupportedPrepareActions(): Long =
            PlaybackStateCompat.ACTION_PREPARE_FROM_MEDIA_ID or
            PlaybackStateCompat.ACTION_PLAY_FROM_MEDIA_ID or
            PlaybackStateCompat.ACTION_PREPARE_FROM_SEARCH or
            PlaybackStateCompat.ACTION_PLAY_FROM_SEARCH


        override fun onPrepare(playWhenReady: Boolean) {
            Log.d(TAG, "[AudioServicePreparer] - onPrepare")
            // TODO: Should get last played station
            onPrepareFromMediaId("<default-media-id>", playWhenReady, null)
        }

        override fun onPrepareFromMediaId(
            mediaId: String,
            playWhenReady: Boolean,
            extras: Bundle?
        ) {
            Log.d(TAG, "[AudioServicePreparer] - onPrepareFromMediaId - $mediaId - playWhenReady? $playWhenReady")
            player.playWhenReady = playWhenReady
            play(mediaId)
        }

        override fun onPrepareFromSearch(query: String, playWhenReady: Boolean, extras: Bundle?) {
            Log.d(TAG, "[AudioServicePreparer] - onPrepareFromSearch")
            // TODO: Should search for station
            play("<default-media-id>")
        }

        override fun onPrepareFromUri(uri: Uri, playWhenReady: Boolean, extras: Bundle?) = Unit
    }

    companion object {
        const val TAG = "PlaybackService"

        const val EVENT_PLAYER_STATE = "onPlayerStateChanged"
        const val EVENT_METADATA = "onMetadataReceived"

        private const val MEDIA_ROOT_ID = "my_root"
        private const val EMPTY_MEDIA_ROOT_ID = "my_empty_root"
    }
}

在 Android Auto 中选择站点时的 Logcat 输出:

2022-11-22 17:11:08.293  2264-2264  PlaybackService         <my-package>                        D  [AudioServicePreparer] - onPrepareFromMediaId - <stream-name> - playWhenReady? true
2022-11-22 17:11:08.304  2264-2264  PlaybackService         <my-package>                        D  Processing stream: <stream-name>
2022-11-22 17:11:08.305  2264-2264  PlaybackService         <my-package>                        D  playStream - sourceUrl: <valid-url>
2022-11-22 17:11:08.395  2264-2264  PlaybackService         <my-package>                        D  PlayerState: Buffering
2022-11-22 17:11:08.397  2264-2476  ReactNativeJS           <my-package>                        I  'STATE:', 'buffering'
2022-11-22 17:11:11.102  2264-2521  DMCodecAdapterFactory   <my-package>                        I  Creating an asynchronous MediaCodec adapter for track type audio
2022-11-22 17:11:11.105  2264-9864  CCodec                  <my-package>                        I  state->set(ALLOCATING)
2022-11-22 17:11:11.105  2264-9864  CCodec                  <my-package>                        I  allocate(c2.android.aac.decoder)
2022-11-22 17:11:11.111  2264-9864  CCodec                  <my-package>                        I  setting up 'default' as default (vendor) store
2022-11-22 17:11:11.114  2264-9864  CCodec                  <my-package>                        I  Created component [c2.android.aac.decoder]
2022-11-22 17:11:11.114  2264-9864  CCodec                  <my-package>                        I  state->set(ALLOCATED)
2022-11-22 17:11:11.115  2264-9864  CCodecConfig            <my-package>                        D  read media type: audio/mp4a-latm
2022-11-22 17:11:11.123  2264-9864  CCodecConfig            <my-package>                        I  query failed after returning 19 values (BAD_INDEX)
2022-11-22 17:11:11.124  2264-9864  MediaCodec              <my-package>                        I  MediaCodec will operate in async mode
2022-11-22 17:11:11.125  2264-9864  CCodec                  <my-package>                        D  [c2.android.aac.decoder] buffers are bound to CCodec for this session
2022-11-22 17:11:11.125  2264-9864  CCodec                  <my-package>                        I  appPid(2264) width(0) height(0)
2022-11-22 17:11:11.125  2264-9864  CCodecConfig            <my-package>                        D  no c2 equivalents for log-session-id
2022-11-22 17:11:11.125  2264-9864  CCodecConfig            <my-package>                        D  no c2 equivalents for flags
2022-11-22 17:11:11.126  2264-9864  CCodecConfig            <my-package>                        D  c2 config diff is   c2::u32 raw.channel-count.value = 2
2022-11-22 17:11:11.126  2264-9864  Codec2Client            <my-package>                        W  query -- param skipped: index = 1107298332.
2022-11-22 17:11:11.126  2264-9864  CCodecConfig            <my-package>                        I  query failed after returning 19 values (BAD_INDEX)
2022-11-22 17:11:11.127  2264-2521  MediaCodec              <my-package>                        D  keep callback message for reclaim
2022-11-22 17:11:11.128  2264-9864  CCodec                  <my-package>                        I  state->set(STARTING)
2022-11-22 17:11:11.132  2264-9864  Codec2Client            <my-package>                        W  query -- param skipped: index = 1342179345.
2022-11-22 17:11:11.132  2264-9864  Codec2Client            <my-package>                        W  query -- param skipped: index = 2415921170.
2022-11-22 17:11:11.132  2264-9864  Codec2Client            <my-package>                        W  query -- param skipped: index = 1610614798.
2022-11-22 17:11:11.135  2264-9864  CCodec                  <my-package>                        I  state->set(RUNNING)
2022-11-22 17:11:11.135  2264-9864  CCodecBufferChannel     <my-package>                        I  [c2.android.aac.decoder#96] 4 initial input buffers available
2022-11-22 17:11:11.136  2264-2980  BufferPoolAccessor2.0   <my-package>                        D  bufferpool2 0xb40000701ca21d68 : 0(0 size) total buffers - 0(0 size) used buffers - 3845/3850 (recycle/alloc) - 5/3845 (fetch/transfer)
2022-11-22 17:11:11.136  2264-2980  BufferPoolAccessor2.0   <my-package>                        D  Destruction - bufferpool2 0xb40000701ca21d68 cached: 0/0M, 0/0% in use; allocs: 3850, 100% recycled; transfers: 3845, 100% unfetched
2022-11-22 17:11:11.140  2264-2264  PlaybackService         <my-package>                        D  PlayerState: Ready
2022-11-22 17:11:11.141  2264-2476  ReactNativeJS           <my-package>                        I  'STATE:', 'ready'
2022-11-22 17:11:11.147  2264-2264  PlaybackService         <my-package>                        D  PlayerState: Playing
2022-11-22 17:11:11.148  2264-2264  PlaybackService         <my-package>                        D  Raw Meta: entries=[PRIV: owner=com.apple.streaming.transportStreamTimestamp]
2022-11-22 17:11:11.149  2264-2264  PlaybackService         <my-package>                        D  Raw Meta: entries=[TIT2: description=null: value=]
2022-11-22 17:11:11.154  2264-9864  CCodecConfig            <my-package>                        D  c2 config diff is   c2::u32 raw.channel-mask.value = 12
2022-11-22 17:11:11.163  2264-2264  PlaybackService         <my-package>                        D  onStartCommand
2022-11-22 17:11:11.172  2264-2521  AudioTrack              <my-package>                        D  setVolume(1.000000, 1.000000) pid : 2264
2022-11-22 17:11:11.238  2264-2521  AudioTrack              <my-package>                        D  getTimestamp_l(74): device stall time corrected using current time 2384346281016
2022-11-22 17:11:11.274  2264-2476  ReactNativeJS           <my-package>                        I  'STATE:', 'playing'
2022-11-22 17:11:11.372  2264-2476  ReactNativeJS           <my-package>                        I  Metadata is undefined
2022-11-22 17:11:12.523  2264-2476  ReactNativeJS           <my-package>                        I  Token is not expired
2022-11-22 17:11:12.651  2264-2478  RNFBCrashlyticsInit     <my-package>                        D  isCrashlyticsCollectionEnabled via RNFBJSON: true
2022-11-22 17:11:12.652  2264-2478  RNFBCrashlyticsInit     <my-package>                        D  isCrashlyticsCollectionEnabled after checking crashlytics_debug_enabled: false
2022-11-22 17:11:12.652  2264-2478  RNFBCrashlyticsInit     <my-package>                        D  isCrashlyticsCollectionEnabled final value: false
2022-11-22 17:11:14.406  2264-2977  CCodecBufferChannel     <my-package>                        D  [c2.android.aac.decoder#96] DEBUG: elapsed: n=6 [in=0 pipeline=0 out=2]
2022-11-22 17:11:16.930  2264-2264  ViewRootIm...nActivity] <my-package>                        I  ViewPostIme pointer 0
2022-11-22 17:11:17.069  2264-2264  ViewRootIm...nActivity] <my-package>                        I  ViewPostIme pointer 1
2022-11-22 17:11:17.082  2264-2264  PlaybackService         <my-package>                        D  Stopping stream.
2022-11-22 17:11:17.086  2264-9864  CCodec                  <my-package>                        I  state->set(FLUSHING)
2022-11-22 17:11:17.086  2264-9864  CCodec                  <my-package>                        I  state->set(FLUSHED)
2022-11-22 17:11:17.087  2264-2521  MediaCodec              <my-package>                        D  keep callback message for reclaim
2022-11-22 17:11:17.087  2264-9864  CCodec                  <my-package>                        I  state->set(RESUMING)
2022-11-22 17:11:17.087  2264-9864  CCodecConfig            <my-package>                        I  query failed after returning 19 values (BAD_INDEX)
2022-11-22 17:11:17.088  2264-9864  CCodec                  <my-package>                        I  state->set(RUNNING)
2022-11-22 17:11:17.089  2264-9864  CCodec                  <my-package>                        I  state->set(RELEASING)
2022-11-22 17:11:17.090  2264-9929  CCodec                  <my-package>                        I  state->set(RELEASED)
2022-11-22 17:11:17.090  2264-9864  hw-BpHwBinder           <my-package>                        I  onLastStrongRef automatically unlinking death recipients
2022-11-22 17:11:17.090  2264-9864  MediaCodec              <my-package>                        I  Codec shutdown complete
2022-11-22 17:11:17.095  2264-2264  PlaybackService         <my-package>                        D  PlayerState: Idle
2022-11-22 17:11:17.097  2264-2476  ReactNativeJS           <my-package>                        I  'STATE:', 'idle'
2022-11-22 17:11:17.097  2264-2264  PlaybackService         <my-package>                        D  PlayerState: Paused (Stopping and clearing)
2022-11-22 17:11:17.201  2264-2476  ReactNativeJS           <my-package>                        I  'STATE:', 'paused'
2022-11-22 17:11:22.137  2264-2979  BufferPoolAccessor2.0   <my-package>                        D  bufferpool2 0xb40000701c6606a8 : 0(0 size) total buffers - 0(0 size) used buffers - 311/316 (recycle/alloc) - 5/311 (fetch/transfer)
2022-11-22 17:11:22.137  2264-2979  BufferPoolAccessor2.0   <my-package>                        D  evictor expired: 1, evicted: 1

流式传输时媒体 session 转储的 Pastebin: https://pastebin.com/5is1h2Mk

如果您认为有任何其他文件可能有助于找到答案,我会尝试提供,但我必须清理一些东西。

最佳答案

我遇到了同样的事情。您在媒体 session 上的播放状态需要调用 setState。您只是在设置播放操作。

关于安卓汽车 : Media item gets stuck "Getting your selection..." instead of showing controls while playing,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/73340801/

相关文章:

android - 安卓汽车和安卓汽车的区别

Android Auto 自定义应用程序 - 官方

android - 如何在高级 Recyclerview 库中插入项目

android - ExoPlayer - 在 DefaultTimeBar 中改变拇指

encryption - 在 exoplayer android 中使用 AES 在线加密流媒体视频文件

android - Exoplayer 无法加载 HLS 视频的字幕

android - 如何设置 Android Auto 媒体应用按钮颜色

android - 无法在 android 4.0 上设置日历事件 >

android - 如何从 Fragment 返回到 Fragment?

android - 在 onLoadFinished() 期间合并游标会在旋转后导致 StaleDataException