zoukankan      html  css  js  c++  java
  • ijkplayer视频播放

     

     
    http://android-doc.com/androiddocs/2017/1018/5416.html
    https://www.2cto.com/kf/201801/714366.html
    https://zhuanlan.zhihu.com/p/25262844
     
    时间:2017-10-18 16:08来源:未知 作者:卓一哥 点击: 1296 次
    图1 视频播放是一个很常见的功能,根据功能需求的不同,有不同的实现方式。 如果只是类似预览的功能,可以直接调取系统的视频播放功能: Intent intent = new Intent(); intent.setAction(Int

    图1图1
    视频播放是一个很常见的功能,根据功能需求的不同,有不同的实现方式。
    如果只是类似预览的功能,可以直接调取系统的视频播放功能:

     

     Intent intent = new Intent();
     intent.setAction(Intent.ACTION_VIEW);
     intent.setDataAndType(Uri.fromFile(new File(path)), "video/mp4");
     activity.startActivity(intent);

    这样做的话,就会跳出App,好处就是用起来简单,坏处就是离开的应用,如果有其他需求的话则无法实现。

    最近的项目中用到的视频播放,有一些特殊的功能,比如不允许用户快进,但是可以退回,用户看过的部分可以快进。要记录播放进度,再次进入时要恢复进度。可以设置断点,断点暂停后用户需要手动点击继续播放。综上,上面的做法就不能用了,只能自己写一个播放器了。

    之前用过Vitamio,整体的使用感觉还是比较顺利,文档示例都比较全。也没有什么大bug。但是商用收费!如果你对Vitamio感兴趣可以看这里

    这次就用了ijkplayer。ijkplayer的文档和示例都没有Vitamio那么多,我是在示例上修修改改的。它是可以支持的在线播放和本地播放的。

    它们都是基于FFmpeg的,你也可以直接干FFmpeg。

    按照ijkplayer的github一步步集成进来,还是比较顺利的。就是这样:

     

    图2图2

     

    官方示例

    上面例子最好down下来,跑一下。

    我用到了一个VideoView来播放视频,它是一个FrameLayout。
    我是在这里扒的,这里代码还用到了其他的调用,一并copy过来,最后是这样的。

    图3图3
    其他的代码你也可以在示例中找到。我把上面的代码放到了自己的项目中。

     

    然后在布局中放入你写(拷)的IjkVideoView。

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:background="@color/black">
    
        <com.greendami.video.widget.media.IjkVideoView
            android:id="@+id/video"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1"></com.greendami.video.widget.media.IjkVideoView>
    
    </LinearLayout>

    如果你只是想简单的播放视频,对界面没有什么要求的话,可以使用ijkplayer提供的MediaController,直接就有进度条,暂停,播放等功能。
    video.setMediaController(AndroidMediaController(this)),不需要的话就传个null进去就行。

    只要这样就行:

    package com.greendami.actvity.worknotes
    
    import android.content.pm.ActivityInfo
    import android.content.res.Configuration
    import android.net.Uri
    import android.os.Environment
    import android.view.WindowManager
    import android.widget.FrameLayout
    import android.widget.LinearLayout
    import com.allrun.dangjianshisanshi.R
    import com.allrun.dangjianshisanshi.actvity.BaseActivity
    import com.allrun.dangjianshisanshi.video.widget.media.AndroidMediaController
    import com.allrun.dangjianshisanshi.widget.LoadingDialog
    import kotlinx.android.synthetic.main.activity_worknote_videoplayer.*
    import org.jetbrains.anko.toast
    import tv.danmaku.ijk.media.player.IjkMediaPlayer
    
    /**
     * Created by greendami on 2017/8/30.
     */
    class WorkNoteVideoPlayerActivity : BaseActivity() {
    
        private val SIZE_DEFAULT = 0
        private val SIZE_4_3 = 1
        private val SIZE_16_9 = 2
        private val currentSize = SIZE_DEFAULT
    
        private var screenWidth = 0
        private var screenHeight = 0
    
    
        ////http://www.modrails.com/videos/passenger_nginx.mov
        var uri = Uri.parse(Environment.getExternalStorageDirectory().path + "/test.mp4")
        var path = ""
    
        override fun setContentView() {
            setContentView(R.layout.activity_worknote_videoplayer)
        }
    
        override fun initView() {
            IjkMediaPlayer.loadLibrariesOnce(null)
            IjkMediaPlayer.native_profileBegin("libijkplayer.so")
            LoadingDialog.showDialog(this)
            initEvent()
        }
    
        private fun initEvent() {
    
            back.setOnClickListener { finish() }
            video.setOnCompletionListener {
                toast("播放完毕")
                finish()
            }
    
            video.setOnPreparedListener {
                LoadingDialog.dismissDialog()
                video.start()
                setVideoLayoutSize()
            }
    
    
        }
    
    
        private fun setVideoLayoutSize() {
            initScreenInfo()
            var width = video.width
            var height = video.height
            if (video.getmVideoWidth() / video.getmVideoHeight() > width / height) {
                height = width * video.getmVideoHeight() / video.getmVideoWidth()
            } else {
                width = height * video.getmVideoWidth() / video.getmVideoHeight()
            }
    
    
            if (width > 0 && height > 0) {
                val lp = video.getmRenderView().view.layoutParams as FrameLayout.LayoutParams
                lp.width = width
                lp.height = height
                video.getmRenderView().view.layoutParams = lp
            }
        }
    
    
        override fun bindData() {
            video.setVideoPath(path)
            video.setMediaController(AndroidMediaController(this))
        }
    
        override fun loadData() {
            path = intent.extras["path"].toString()
        }
    
        private fun setScreenRate(newConfig: Configuration) {
            var width = 0
            var height = 0
            if (newConfig.orientation === Configuration.ORIENTATION_LANDSCAPE) {//切换为横屏
                when (currentSize) {
                    SIZE_DEFAULT -> {
                        width = video.getmVideoWidth()
                        height = video.getmVideoHeight()
                    }
                    SIZE_4_3 -> {
                        width = screenHeight / 3 * 4
                        height = screenHeight
                    }
                    SIZE_16_9 -> {
                        width = screenHeight / 9 * 16
                        height = screenHeight
                    }
                }
            } else { //竖屏
    
                when (currentSize) {
                    SIZE_DEFAULT -> {
                        width = video.getmVideoWidth()
                        height = video.getmVideoHeight()
                    }
                    SIZE_4_3 -> {
                        width = screenWidth
                        height = screenWidth * 3 / 4
                    }
                    SIZE_16_9 -> {
                        width = screenWidth
                        height = screenWidth * 9 / 16
                    }
                }
            }
            if (width > 0 && height > 0) {
                val lp = video.getmRenderView().view.layoutParams as FrameLayout.LayoutParams
                lp.width = width
                lp.height = height
                video.getmRenderView().view.layoutParams = lp
            }
    
        }
    
        private fun fullChangeScreen() {
            requestedOrientation = if (requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE) {// 切换为竖屏
                ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
            } else {
                ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
            }
        }
    
        override fun onConfigurationChanged(newConfig: Configuration) {
            super.onConfigurationChanged(newConfig)
            //重新获取屏幕宽高
            initScreenInfo()
            if (newConfig.orientation === Configuration.ORIENTATION_LANDSCAPE) {//切换为横屏
    
                //去掉通知栏
                //获得 WindowManager.LayoutParams 属性对象
                val lp2 = window.attributes
                //直接对它flags变量操作   LayoutParams.FLAG_FULLSCREEN 表示设置全屏
                lp2.flags = lp2.flags or WindowManager.LayoutParams.FLAG_FULLSCREEN
                //设置属性
                window.attributes = lp2
                //意思大致就是  允许窗口扩展到屏幕之外
                window.addFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS)
    
    
                val lp = video.layoutParams as LinearLayout.LayoutParams
                lp.height = screenHeight
                lp.width = screenWidth
                video.layoutParams = lp
            } else {
    
                //恢复通知栏
                //获得 WindowManager.LayoutParams 属性对象
                val lp2 = window.attributes
                //LayoutParams.FLAG_FULLSCREEN 强制屏幕状态条栏弹出
                lp2.flags = lp2.flags and WindowManager.LayoutParams.FLAG_FULLSCREEN.inv()
                //设置属性
                window.attributes = lp2
                //不允许窗口扩展到屏幕之外  clear掉了
                window.clearFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS)
    
    
                val lp = video.layoutParams as LinearLayout.LayoutParams
                when (currentSize) {
                    SIZE_DEFAULT -> {
                        lp.height = video.getmVideoHeight() * screenWidth / video.getmVideoWidth()
                    }
                    SIZE_4_3 -> {
                        lp.height = screenWidth * 3 / 4
                    }
                    SIZE_16_9 -> {
                        lp.height = screenWidth * 9 / 16
                    }
                }
    
                lp.width = screenWidth
                video.layoutParams = lp
            }
            setScreenRate(newConfig)
        }
    
        private fun initScreenInfo() {
            val wm = this.windowManager
    
            screenWidth = wm.defaultDisplay.width
            screenHeight = wm.defaultDisplay.height
        }
    
    
        override fun onDestroy() {
            video.release(true)
            LoadingDialog.dismissDialog()
            super.onDestroy()
        }
    }

    如果不把BaseActivity放上来,可能看起来费劲,这个是我随便写的,请不要在意。

    abstract class BaseActivity : LifecycleActivity() {
        lateinit var modelHolder: ModelHolder
        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            modelHolder = ViewModelProviders.of(this).get(ModelHolder::class.java)
            setContentView()
            loadData()
            initView()
            bindData()
        }
    
        abstract fun setContentView()
    
        /**
         * 请求数据
         */
        abstract fun loadData()
    
        abstract fun initView()
    
        /**
        * 把数据和控件绑定
        */
        abstract fun bindData()
    
    }

    大体的步骤是:

    //加载库文件
    IjkMediaPlayer.loadLibrariesOnce(null)
    IjkMediaPlayer.native_profileBegin("libijkplayer.so")
    
    //设置文件路径可以是网络地址或者本地文件路径
    video.setVideoPath(path)
    //使用默认的控制界面,进度条快进等等
    video.setMediaController(AndroidMediaController(this))
    
    //绑定加载完成监听器,加载完了就播放
    video.setOnPreparedListener {
            LoadingDialog.dismissDialog()
            video.start()
            //这里是设置视频的尺寸,不是必须
            setVideoLayoutSize()
        }
    //到此就完成了,如果你需要重力感应,全屏切换,请往下看
    
    //如果按钮切换横竖屏,调用这个方法
    fullChangeScreen()
    //这里是横竖屏切换事件的回调
    override fun onConfigurationChanged(newConfig: Configuration)
    
    //这里是屏幕方向改变后重新计算视频尺寸,我是默认不改变视频长宽比的前提下铺满屏幕
    //video.getmVideoWidth()是视频的尺寸,是我自己加的方法,只是返回了tmVideoWidth
    //video.getmRenderView().view.layoutParams = lp这句是真正设置视频尺寸
    private fun setScreenRate(newConfig: Configuration) {
            initScreenInfo()
            var width = screenWidth
            var height = screenHeight
            if (video.getmVideoWidth() / video.getmVideoHeight() > width / height) {
                height = width * video.getmVideoHeight() / video.getmVideoWidth()
            } else {
                width = height * video.getmVideoWidth() / video.getmVideoHeight()
            }
            if (width > 0 && height > 0) {
                val lp = video.getmRenderView().view.layoutParams as FrameLayout.LayoutParams
                lp.width = width
                lp.height = height
                video.getmRenderView().view.layoutParams = lp
            }
    
        }

    如果你需要控制视频,下面的API你可能会用到:

    //进度控制
    video.seekTo(progress)
    //视频播放完毕回调
    video.setOnCompletionListener {toast("播放完毕")}
    //视频长度
    video.duration
    //暂停
    video.pause()
    //继续播放
    if (!video.isPlaying) video.start();

    http://android-doc.com/androiddocs/2017/1018/5416.html
  • 相关阅读:
    LeetCode 275. H-Index II
    LeetCode 274. H-Index
    LeetCode Gray Code
    LeetCode 260. Single Number III
    LeetCode Word Pattern
    LeetCode Nim Game
    LeetCode 128. Longest Consecutive Sequence
    LeetCode 208. Implement Trie (Prefix Tree)
    LeetCode 130. Surrounded Regions
    LeetCode 200. Number of Islands
  • 原文地址:https://www.cnblogs.com/pengmn/p/9068094.html
Copyright © 2011-2022 走看看