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

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

文章目录

  • 需求:
  • 场景
  • 实现方案
  • 遇到的坑
    • 播放器选择
    • 界面显示不全
    • 视频友好显示问题
    • 缓存
  • 总结


需求:

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

场景

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

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

实现效果如下
在这里插入图片描述

实现方案

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

遇到的坑

播放器选择

最开始用得饺子播放器和Ex播放器封装sdk,遇到加载时候默认预览画面、默认有电量、wifi 控制显示等。 针对于在线视频播放非常实用,并不适合本地视频简单播放。
可以尝试用的情况下,直接用依赖包不方便,拿到依赖包源码放到本地,需要去掉默认很多选项,太折腾。
各种配置针对本地播放么有那么麻烦,最终就用VideoView 来实现
部分封装播放器存在对本地视频文件的不友好,如饺子播放器无法播放本地视频的。

界面显示不全

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

视频友好显示问题

存在视频显示不全、比例对比、压缩变形问题。 无外乎进行参数配置,裁剪等。特别是第三方播放器 用sdk 来进行配置必须的。
最简单方案:横竖屏两套视频,横竖屏情况下播放的其实是不同资源相同画面的视频。

在这里插入图片描述

缓存

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

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

class VideoViewPlayFragment : Fragment() {private val TAG = "VideoViewPlayFragment"var pathUrl: String = ""private lateinit var viewModel: VideoPlayViewModelprivate lateinit var viewBindingFragmentVideoPlayBinding: FragmentVideoviewPlayBindingprivate val binding get() = viewBindingFragmentVideoPlayBinding!!companion object {fun newInstance(videoPath: String): VideoViewPlayFragment {val fragment = VideoViewPlayFragment()val args = Bundle()args.putString("pathUrl", videoPath)fragment.arguments = argsLog.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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/899284.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

从零构建大语言模型全栈开发指南:第二部分:模型架构设计与实现-2.2.2文本生成逻辑:Top-k采样与温度控制

👉 点击关注不迷路 👉 点击关注不迷路 👉 点击关注不迷路 文章大纲 2.2.2 文本生成逻辑:Top-k采样与温度控制1. 文本生成的核心挑战与数学框架1.1 自回归生成的基本流程2. `Top-k`采样原理与工程实现2.1 数学定义与算法流程2.2 PyTorch实现优化3. 温度控制的数学本质与参…

为什么后端接口返回数字类型1.00前端会取到1?

这得从axios中得默认值说起: Axios 的 transformResponse axios 在接收到服务器的响应后,会通过一系列的转换函数(transformResponse)来处理响应数据,使其适合在应用程序中使用。默认情况下,axios 的 tran…

【C++游戏引擎开发】《线性代数》(2):矩阵加减法与SIMD集成

一、矩阵加减法数学原理 1.1 定义 ​逐元素操作:运算仅针对相同位置的元素,不涉及矩阵乘法或行列变换。​交换律与结合律: 加法满足交换律(A + B = B + A)和结合律( ( A + B ) + C = A + ( B + C ) )。 ​减法不满足交换律(A − B ≠ B − A)。1.2 公式 ​ C i j = …

openGauss关联列数据类型不一致引起谓词传递失败

今天分享一个比较有意思的案例 注意:因为原始SQL很长,为了方便排版,简化了SQL 下面SQL跑60秒才出结果,客户请求优化 select dtcs.owner, dtcs.table_name, dtcs.column_name, dct.commentsfrom dba_tab_columns dtcsleft outer j…

01 相机标定与相机模型介绍

学完本文,您将了解不同相机模型分类、内参意义,及对应的应用代码模型 标定的意义 建模三维世界点投影到二维图像平面的过程。标定输出的是相机模型。 相机模型 相机模型可以解理解为投影模型 +

Hyperlane:Rust Web开发的未来,释放极致性能与简洁之美

Hyperlane:Rust Web开发的未来,释放极致性能与简洁之美 你是否厌倦了复杂的Web框架,想要一个既高效又易用的工具来构建现代Web应用?Hyperlane正是你需要的答案!作为专为Rust打造的轻量级、高性能HTTP服务器库&#xf…

STM32学习笔记之振荡器(原理篇)

📢:如果你也对机器人、人工智能感兴趣,看来我们志同道合✨ 📢:不妨浏览一下我的博客主页【https://blog.csdn.net/weixin_51244852】 📢:文章若有幸对你有帮助,可点赞 👍…

Stereolabs ZED Box Mini:机器人与自动化领域的人工智能视觉新选择

在人工智能视觉技术快速发展的今天,其应用场景正在持续拓宽,从智能安防到工业自动化,从机器人技术到智能交通,各领域都在积极探索如何利用这一先进技术。而 Stereolabs 推出的ZED Box Mini,正是一款专为满足这些多样化…

K8S学习之基础五十九:部署gitlab服务

部署gitlab docker pull gitlab/gitlab-ce:latest docker tag gitlab/gitlab-ce:latest 172.16.80.140/gitlab/gitlab-ce:latest docker push 172.16.80.140/gitlab/gitlab-ce:latest docker run -d -p 443:443 -p 80:80 -p 222:22 --name gitlab --restart always -v /home/…

多线程 --- 多线程编程

在写代码的时候,可以使用多进程进行并发编程(在Java中,不太推荐,很多很多关于进程相关的API,在Java标准库中,都没有提供),也可以使用多线程进行并发编程(系统提供了多线程…

HTML~视频音频在网页中不能自动播放

问题: autoplay是打开自动播放,但是发现加了关键词还是没有反应 原因: 现在浏览器禁止自动播放(特别是带声音的) 解决办法: 可以添加muted 进行没有声音的自动播放

vue中上传接口file表单提交二进制文件流

1.使用elementui上传组件 要做一个选择文件后&#xff0c;先不上传&#xff0c;等最后点击确定后&#xff0c;把file二进制流及附加参数一起提交上去。 首先使用elementui中的上传组件&#xff0c;设置auto-uploadfalse&#xff0c;也就是选择文件后不立刻上传。 <el-uplo…

Flutter环境配置

配置环境变量 PUB_HOSTED_URLhttps://pub.flutter-io.cnFLUTTER_STORAGE_BASE_URLhttps://storage.flutter-io.cn 这个命令是用来配置 Flutter 的镜像源地址&#xff0c;主要是为了解决在中国大陆地区访问 Flutter 官方资源较慢的问题。 具体的操作如下&#xff1a; 右键点…

ngx_http_index_set_index

定义在 src\http\modules\ngx_http_index_module.c static char * ngx_http_index_set_index(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) {ngx_http_index_loc_conf_t *ilcf conf;ngx_str_t *value;ngx_uint_t i, n;ngx_http_inde…

数据库的视图有什么用?

数据库的视图&#xff08;View&#xff09;是一种虚拟表&#xff0c;它的内容由查询定义&#xff0c;并不实际存储数据&#xff0c;而是动态生成。视图的主要作用如下&#xff1a; 1. 简化复杂查询 场景&#xff1a;当查询涉及多表连接、复杂过滤或聚合操作时&#xff0c;SQL …

Ubuntu 24.04 安装 Docker 详细教程

前言 Docker 是目前最流行的容器化技术&#xff0c;它可以帮助开发者快速部署和运行应用程序。本文将详细介绍在 Ubuntu 24.04 (Noble Numbat) 上安装 Docker 的完整步骤&#xff0c;包括配置镜像加速等实用技巧。 一、准备工作 1.1 系统要求 Ubuntu 24.04 LTS 具有 sudo 权…

鸿蒙进行视频上传,使用 request.uploadFile方法

一.拉起选择器进行视频选择&#xff0c;并且创建文件名称 async getPictureFromAlbum() {// 拉起相册&#xff0c;选择图片let PhotoSelectOptions new photoAccessHelper.PhotoSelectOptions();PhotoSelectOptions.MIMEType photoAccessHelper.PhotoViewMIMETypes.VIDEO_TY…

C++ map容器总结

map基本概念 简介&#xff1a; map中所有元素都是pair pair中第一个元素为key&#xff08;键值&#xff09;&#xff0c;起到索引作用&#xff0c;第二个元素为value&#xff08;实值&#xff09; 所有元素都会根据元素的键值自动排序 本质&#xff1a; map/multimap属于关…

【Zookeeper搭建(跟练版)】Zookeeper分布式集群搭建

&#xff08;一&#xff09;克隆前的准备 1. 用 xftp 发送文件 2. 时间同步&#xff1a; sudo cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime 3. zookpeeper 安装 部署 呼应开头发送的压缩包&#xff0c;解压&#xff1a; cd ~ tar -zxvf zookeeper-3.4.6.tar.g…

Flutter项目之页面实现以及路由fluro

目录&#xff1a; 1、项目代码结构2、页面编写以及路由配置main.dart(入口文件)page_content.dartindex.dart&#xff08;首页&#xff09;application.dart&#xff08;启动加载类&#xff09;pubspec.yaml&#xff08;依赖配置文件&#xff09;login.dart&#xff08;登录页&…