Compose笔记(十六)--ExoPlayer

        这一节了解一下Compose中的ExoPlayer的使用,我们在开发Android应用时,经常会使用到播放器,这个ExoPlayer框架就相对成熟,易上手,现简单总结如下:

1. ExoPlayer 核心类
       ExoPlayer 是 ExoPlayer库的核心类,负责管理媒体播放的整个生命周期,包括加载、播放、暂停、停止等操作。
       作用:1 创建播放器实例。2 管理播放状态(播放、暂停、停止)。3 控制播放进度(如跳转到指定位置)。4 监听播放事件(如播放完成、错误等)。
2. SimpleExoPlayer
      SimpleExoPlayer 是 ExoPlayer 的一个简化实现,适用于大多数基本播放场景。
      作用:1 提供了更简单的 API 来管理播放。2 支持音频和视频播放。3 通常用于不需要复杂自定义的场景。
3. Player.Listener
      含义:Player.Listener 是 ExoPlayer 的监听器接口,用于接收播放状态变化和事件通知。
      作用:1 监听播放状态变化(如播放、暂停、停止)。2 监听播放进度变化。3 监听错误事件。4 监听媒体加载状态(如缓冲完成)。
4. MediaItem
      含义:MediaItem 表示要播放的媒体资源,可以是本地文件或网络资源。
      作用:1 定义媒体资源的 URI(如本地路径或网络 URL)。2 设置媒体元数据(如标题、描述等)。3添加到播放列表中。
5. PlayerView
      含义:PlayerView 是 ExoPlayer 提供的 UI 组件,用于显示视频画面和控制播放。
     作用:1 显示视频画面。2 提供内置的播放控制按钮(播放、暂停、进度条等)。3 可以通过Compose的AndroidView或ComposeView集成到ComposeUI中。
6. 播放控制方法
play():开始播放。
pause():暂停播放。
stop():停止播放并重置播放器状态。
seekTo(position: Long):跳转到指定位置(以毫秒为单位)。
setPlayWhenReady(playWhenReady: Boolean):设置播放器是否准备好时自动播放。
7. 播放状态
ExoPlayer 提供了多种播放状态,可以通过 Player.State 或 playbackState 属性获取:
STATE_IDLE:播放器空闲状态,未准备播放。
STATE_BUFFERING:播放器正在缓冲数据。
STATE_READY:播放器已准备好,可以播放。
STATE_ENDED:播放结束。
8. 事件监听(如播放完成)
含义:通过监听器可以捕获播放完成事件。

栗子

app模块下gradle:

implementation ("com.google.android.exoplayer:exoplayer-core:2.19.1")
implementation ("com.google.android.exoplayer:exoplayer-hls:2.19.1")
implementation ("com.google.android.exoplayer:exoplayer-ui:2.19.1")
implementation("androidx.preference:preference-ktx:1.2.1")
<activity
            android:name=".testexoplayer.YourVideoActivity"
            android:exported="true"
            android:theme="@style/Theme.ComposeNavigationDemo"
            android:supportsPictureInPicture="true"
            android:configChanges="screenSize|smallestScreenSize|screenLayout|orientation">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
package com.example.composenavigationdemo.testexoplayer

import android.content.Context
import android.content.pm.ActivityInfo
import android.os.Bundle
import android.view.ViewGroup
import android.widget.FrameLayout
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.Button
import androidx.compose.material3.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.viewinterop.AndroidView
import com.google.android.exoplayer2.ExoPlayer
import com.google.android.exoplayer2.MediaItem
import com.google.android.exoplayer2.ui.StyledPlayerView
import androidx.preference.PreferenceManager

class YourVideoActivity : ComponentActivity() {
    private lateinit var exoPlayer: ExoPlayer
    private val PREF_KEY_PLAYBACK_POSITION = "playback_position"

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            VideoPlayerScreen()
        }
    }

    @Composable
    fun VideoPlayerScreen() {
        val videoUrl = "https://vdept3.bdstatic.com/mda-rbq74um403w6qq1c/cae_h264/1740460540715545764/mda-rbq74um403w6qq1c.mp4?v_from_s=hkapp-haokan-suzhou&auth_key=1743250028-0-0-dbffc551f0aea2bffc89d537dba36202&bcevod_channel=searchbox_feed&cr=0&cd=0&pd=1&pt=3&logid=0428327789&vid=10916160709390273754&klogid=0428327789&abtest=\n" 
        var isPlaying by remember { mutableStateOf(true) }
        val context = LocalContext.current

        val playerView = remember {
            StyledPlayerView(this).apply {
                layoutParams = FrameLayout.LayoutParams(
                    ViewGroup.LayoutParams.MATCH_PARENT,
                    ViewGroup.LayoutParams.MATCH_PARENT
                )
            }
        }

        val player = remember {
            ExoPlayer.Builder(this).build().apply {
                setMediaItem(MediaItem.fromUri(videoUrl))
                // 读取播放进度
                val playbackPosition = getPlaybackPosition(context)
                if (playbackPosition > 0) {
                    seekTo(playbackPosition)
                }
                prepare()
                playWhenReady = isPlaying
            }
        }

        DisposableEffect(Unit) {
            playerView.player = player
            onDispose {
                // 保存播放进度
                savePlaybackPosition(context, player.currentPosition)
                player.release()
            }
        }

        Box(modifier = Modifier.fillMaxSize()) {
            AndroidView(
                factory = { playerView },
                modifier = Modifier.fillMaxSize()
            )

            Button(
                onClick = {
                    if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
                        enterPictureInPictureMode()
                    }
                },
                modifier = Modifier.align(androidx.compose.ui.Alignment.BottomEnd)
            ) {
                Text(text = "进入画中画")
            }
        }
    }

    private fun savePlaybackPosition(context: Context, position: Long) {
        val prefs = PreferenceManager.getDefaultSharedPreferences(context)
        prefs.edit().putLong(PREF_KEY_PLAYBACK_POSITION, position).apply()
    }

    private fun getPlaybackPosition(context: Context): Long {
        val prefs = PreferenceManager.getDefaultSharedPreferences(context)
        return prefs.getLong(PREF_KEY_PLAYBACK_POSITION, 0)
    }

    override fun onPictureInPictureModeChanged(
        isInPictureInPictureMode: Boolean,
        newConfig: android.content.res.Configuration
    ) {
        super.onPictureInPictureModeChanged(isInPictureInPictureMode, newConfig)
        if (isInPictureInPictureMode) {
            // 进入画中画模式
            requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE
        } else {
            // 退出画中画模式
//            requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED
            requestedOrientation = ActivityInfo.SCREEN_O RIENTATION_SENSOR_PORTRAIT
        }
    }
}

分析:上面实现了一个画中画的播放效果。

注意:

1. 依赖管理与版本选择
版本兼容性:ExoPlayer 的版本需要与项目的 Android Gradle 插件版本和 Compose 版本兼容,避免因版本不匹配导致的问题
2. 生命周期管理
释放资源:ExoPlayer是资源密集型组件,必须在不再使用时释放资源。可以通过DisposableEffect或onDispose 来管理生命周期,确保在onStop或onDestroy时调用player.release()。

DisposableEffect(lifecycleOwner) {
    onDispose {
        exoPlayer.release()
    }
}

3. 媒体源设置
动态设置媒体源:在播放不同媒体时,需要动态设置 MediaItem,并调用 player.setMediaItem() 和 player.prepare()。

4. 线程与异步操作
后台线程加载:避免在主线程中进行媒体加载或解码操作,ExoPlayer会自动处理大部分异步操作,但确保网络请求或大数据处理在后台线程中完成。
异步准备:如果需要预加载媒体,可以在后台线程中调用 player.prepare(),避免阻塞UI。

### ExoPlayer 官方文档及使用教程 ExoPlayerAndroid 平台上的一款功能强大的媒体播放器库,支持多种媒体格式和流式传输协议。以下是关于 ExoPlayer 的官方文档、示例代码以及常见问题的解决方案。 #### 1. 官方文档 ExoPlayer 提供了详细的官方文档,涵盖了入门指南、媒体类型支持以及高级主题。以下是主要文档的链接: - **入门指南**:介绍如何开始使用 ExoPlayer,包括设置开发环境和基本用法[^1]。 - **媒体类型支持**:描述 ExoPlayer 支持的不同媒体格式及其配置方法[^1]。 - **高级主题**:涵盖自定义播放器、扩展功能和性能优化等内容[^1]。 #### 2. 示例代码 以下是一个简单的 ExoPlayer 使用示例,展示如何创建一个播放器并播放指定的媒体文件。 ```java import com.google.android.exoplayer2.ExoPlayer; import com.google.android.exoplayer2.MediaItem; public class SimpleExoPlayerExample { public void initializePlayer(Context context) { // 创建 ExoPlayer 实例 ExoPlayer player = new ExoPlayer.Builder(context).build(); // 设置数据源(例如,播放 URL) String videoUrl = "https://example.com/sample.mp4"; MediaItem mediaItem = MediaItem.fromUri(videoUrl); // 将媒体项添加到播放器 player.setMediaItem(mediaItem); // 准备并开始播放 player.prepare(); player.play(); } } ``` #### 3. 常见问题及解决方案 - **硬解码失败问题**:如果在某些设备上遇到硬解码失败的问题(如 `OMX.google.mp3.decoder` 错误),可以通过集成 FFmpeg 扩展模块实现软解码[^4]。具体步骤如下: - 添加 FFmpeg 扩展依赖到 `build.gradle` 文件中: ```gradle implementation 'com.google.android.exoplayer:extension-ffmpeg:2.X.X' ``` - 配置播放器以使用 FFmpeg 解码器。 - **项目启动流程**:ExoPlayer 的模块化设计使其可以灵活定制。通常需要完成以下几个关键步骤[^2]: - 导入必要的 ExoPlayer 库。 - 创建 `SimpleExoPlayer` 实例。 - 设置数据源(如通过 `MediaItem` 指定播放 URL 或本地文件路径)。 - 调用 `player.prepare()` 和 `player.play()` 开始播放。 - **开源项目参考**:可以从 GitHub 克隆一个完整的 ExoPlayer 示例项目,帮助快速上手[^5]: ```bash git clone https://github.com/yusufcakmak/ExoPlayerSample.git ``` #### 4. 关于 TextureView 的使用 如果需要在活动中集成 ExoPlayer 并使用 `TextureView` 显示视频,可以参考以下开源项目的实现[^3]: - **TextureViewSampleActivity.java**:展示了如何将 ExoPlayer 与 `TextureView` 结合使用。 - **build.gradle**:包含导入 ExoPlayer 库的依赖声明。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值