播放本地视频-实现视频画廊功能

实现一个视频画廊,播放本地视频 可以切换不同视频的功能

文章目录


需求:

实现一个视频画廊,播放本地视频 可以切换不同视频的功能

场景

图片画廊的场景很多,视频也一样,主要商显上面用得后很多。

  • 广告机、各种大屏广告显示提醒
  • 宣传-会展用的需求
  • 壁画功能,如家庭相框也可以一直播放视频使用

实现效果如下

实现方案

  • 加载页面滑动实现:viewPager+Fragment 方案
  • 视频布局和播放组件用:VideoView
  • 最简单的 VideoView 来实现

遇到的坑

播放器选择

最开始用得饺子播放器和Ex播放器封装sdk,遇到加载时候默认预览画面、默认有电量、wifi 控制显示等。 针对于在线视频播放非常实用,并不适合本地视频简单播放。

可以尝试用的情况下,直接用依赖包不方便,拿到依赖包源码放到本地,需要去掉默认很多选项,太折腾。

各种配置针对本地播放么有那么麻烦,最终就用VideoView 来实现

部分封装播放器存在对本地视频文件的不友好,如饺子播放器无法播放本地视频的。

界面显示不全

遇到界面显示不全问题,横竖屏切换时候。 用横竖屏两套布局文件来实现即可。

视频友好显示问题

存在视频显示不全、比例对比、压缩变形问题。 无外乎进行参数配置,裁剪等。特别是第三方播放器 用sdk 来进行配置必须的。

最简单方案:横竖屏两套视频,横竖屏情况下播放的其实是不同资源相同画面的视频。

缓存

如同抖音播放在线视频一样,一定要尽量提前缓存,或者播放之后进行缓存一次。 不然切换不同视频资源播放的时候 也就是 切换不同画面播放视频时候回黑以下。

部分源码分享如下,仅提供播放逻辑而已。

java 复制代码
class VideoViewPlayFragment : Fragment() {


    private val TAG = "VideoViewPlayFragment"
    var pathUrl: String = ""
    private lateinit var viewModel: VideoPlayViewModel
    private lateinit var viewBindingFragmentVideoPlayBinding: FragmentVideoviewPlayBinding
    private val binding get() = viewBindingFragmentVideoPlayBinding!!


    companion object {
        fun newInstance(videoPath: String): VideoViewPlayFragment {
            val fragment = VideoViewPlayFragment()
            val args = Bundle()
            args.putString("pathUrl", videoPath)
            fragment.arguments = args

            Log.d("VideoViewPlayFragment"," newInstance videoPath:${videoPath}")
            return fragment
        }
    }

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        viewBindingFragmentVideoPlayBinding =
            FragmentVideoviewPlayBinding.inflate(inflater, container, false)
        pathUrl = arguments?.getString("pathUrl").toString()
        Log.d(TAG, "   传递过来的数据:pathUrl:$pathUrl")
        Log.d(TAG," onCreateView videoPath:${pathUrl}")
        parseUrl(pathUrl)
        return binding.root
    }
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        Log.d(TAG," onCreate onCreate:${pathUrl}")
        viewModel = ViewModelProvider(this).get(VideoPlayViewModel::class.java)
    }

    private fun parseUrl(pathUrl: String) {
        Log.d(TAG," parseUrl  pathUrl value:${pathUrl}  ")
        when (pathUrl) {
            "beach" -> {
                if (isLandscape()) {
                    videoPlay("android.resource://" + ContextProvider.get().context.packageName + "/" + R.raw.beach_h)
                } else {
                    videoPlay("android.resource://" + ContextProvider.get().context.packageName + "/" + R.raw.beach_s)
                }
            }
            "bird" -> {
                if (isLandscape()) {
                    videoPlay("android.resource://" + ContextProvider.get().context.packageName + "/" + R.raw.bird_h)
                } else {
                    videoPlay("android.resource://" + ContextProvider.get().context.packageName + "/" + R.raw.bird_s)
                }
            }
            "fire" -> {
                if (isLandscape()) {
                    videoPlay("android.resource://" + ContextProvider.get().context.packageName + "/" + R.raw.fire_new_h)
                } else {
                    videoPlay("android.resource://" + ContextProvider.get().context.packageName + "/" + R.raw.fire_new_s)
                }
            }
            "flower" -> {
                if (isLandscape()) {
                    videoPlay("android.resource://" + ContextProvider.get().context.packageName + "/" + R.raw.flower_h)
                } else {
                    videoPlay("android.resource://" + ContextProvider.get().context.packageName + "/" + R.raw.flower_s)
                }
            }
            "fog" -> {
                if (isLandscape()) {
                    videoPlay("android.resource://" + ContextProvider.get().context.packageName + "/" + R.raw.fog_h)
                } else {
                    videoPlay("android.resource://" + ContextProvider.get().context.packageName + "/" + R.raw.fog_s)
                }
            }
            "rain" -> {
                if (isLandscape()) {
                    videoPlay("android.resource://" + ContextProvider.get().context.packageName + "/" + R.raw.rain_h)
                } else {
                    videoPlay("android.resource://" + ContextProvider.get().context.packageName + "/" + R.raw.rain_s)
                }
            }
            "snow" -> {
                if (isLandscape()) {
                    videoPlay("android.resource://" + ContextProvider.get().context.packageName + "/" + R.raw.snow_h)
                } else {
                    videoPlay("android.resource://" + ContextProvider.get().context.packageName + "/" + R.raw.snow_s)
                }
            }

            "summer" -> {
                if (isLandscape()) {
                    videoPlay("android.resource://" + ContextProvider.get().context.packageName + "/" + R.raw.summer_h)
                } else {
                    videoPlay("android.resource://" + ContextProvider.get().context.packageName + "/" + R.raw.summer_s)
                }
            }
        }
    }

    fun videoPlay(path1: String) {
        Log.d(TAG,"videoPlay    path1:${path1}")
        viewBindingFragmentVideoPlayBinding.sourceVideo.setVideoURI(Uri.parse(path1))
        //viewBindingFragmentVideoPlayBinding.sourceVideo.start()
        viewBindingFragmentVideoPlayBinding.sourceVideo.setOnCompletionListener(MediaPlayer.OnCompletionListener {
            viewBindingFragmentVideoPlayBinding.sourceVideo.setVideoURI(Uri.parse(path1))
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                viewBindingFragmentVideoPlayBinding.sourceVideo.setAudioFocusRequest(AudioManager.AUDIOFOCUS_NONE)
            }
            Log.d(TAG, " OnCompletionListener  播放完成了  继续播放... ")
            viewBindingFragmentVideoPlayBinding.sourceVideo.start()
        })
        viewBindingFragmentVideoPlayBinding.sourceVideo.setOnErrorListener(MediaPlayer.OnErrorListener { mp, what, extra ->
            Log.d(TAG, " OnErrorListener 播放异常了... ")
            true
        })
    }


    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)
    }

    override fun onConfigurationChanged(newConfig: Configuration) {
        super.onConfigurationChanged(newConfig)
        parseUrl(pathUrl)
    }

    override fun onResume() {
        super.onResume()
        Log.d(TAG, " onResume   pathUrl:$pathUrl")
        if (!viewBindingFragmentVideoPlayBinding.sourceVideo.isPlaying()) {
            Log.d(TAG, "  开始播放")
            viewBindingFragmentVideoPlayBinding.sourceVideo.start()
        }
    }


    override fun onPause() {
        super.onPause()
        if (viewBindingFragmentVideoPlayBinding.sourceVideo.isPlaying()) {
            Log.d(TAG, "  暂停播放")
            viewBindingFragmentVideoPlayBinding.sourceVideo.pause()
        }
    }
}

最后显示效果就很nice 了。

总结

针对市场上面播放显示效果做了一个简单的总结 和 避坑指南,实际效果确实不一样,很nice

相关推荐
AI创界者1 小时前
前沿多模态大模型 MiniMax-H3无审查 本地部署与深度实测指南
人工智能·aigc·音视频
世岩清上6 小时前
展厅数字内容同质化严重,怎样打造专属叙事风格?
大数据·前端·javascript·人工智能·html·音视频·展厅改造
LCG元7 小时前
STM32F407 I2S驱动WM8978音频播放实战:PLLI2S时钟整定、DMA双缓冲防爆音与WAV采样率实测
stm32·嵌入式硬件·音视频
jbk33118 小时前
PixiuCut「视频批量分割」支持智能镜头分割、按时长、按数量分割,可定义最小时长避免无效片段等多可选参数可设置
人工智能·音视频·剪辑软件·剪映自动化软件
投票竞赛8 小时前
作品投票版权注意事项,线上评选上传图片视频须知
python·音视频
byte轻骑兵11 小时前
【AVDTP】规范精讲[13]: 吃透内容保护机制,打通蓝牙音视频版权加密全流程
人工智能·音视频·avrcp·蓝牙耳机·蓝牙音频
世岩清上13 小时前
文旅历史展厅纪录片式视频,怎样弱化说教感提升自主观看欲?
人工智能·音视频·宣传片·展厅改造
言乐613 小时前
Python加速器2视频网页加速器
前端·javascript·css·python·音视频
RobinDevNotes13 小时前
Palmier Pro:AI时代的Mac视频编辑器
人工智能·ceph·macos·ai·音视频·视频编辑·mcp