Android_Watching video freeze causes and solutions

ijkplayer's github address

Compilation of ijkplayer

gradle

Now you can import ijkplayer directly using gradle. However, if you need to support more formats, you still need to compile it yourself.

allprojects {
    repositories {
        jcenter()
    }
}

dependencies {
    # required, enough for most devices.
    compile 'tv.danmaku.ijk.media:ijkplayer-java:0.8.1.2'
    compile 'tv.danmaku.ijk.media:ijkplayer-armv7a:0.8.1.2'

    # Other ABIs: optional
    compile 'tv.danmaku.ijk.media:ijkplayer-armv5:0.8.1.2'
    compile 'tv.danmaku.ijk.media:ijkplayer-arm64:0.8.1.2'
    compile 'tv.danmaku.ijk.media:ijkplayer-x86:0.8.1.2'
    compile 'tv.danmaku.ijk.media:ijkplayer-x86_64:0.8.1.2'

    # ExoPlayer as IMediaPlayer: optional, experimental
    compile 'tv.danmaku.ijk.media:ijkplayer-exo:0.8.1.2'
}
compile

If we need to get more video format support (such as mkv, rmvb, etc.), we need to compile it ourselves. I have a finished product here, including a small demo, students who don't want to compile can mention it by themselves. github address .

I am compiling under Ubuntu. The specific method is available on the official GitHub. I will summarize it. Basically, it is to enter the command into the terminal:

  1. Configure Android sdk and ndk under ubuntu by yourself.

  2. Install git and yasm. Open a terminal and enter the following commands in sequence:

sudo apt-get update
sudo apt-get install git
sudo apt-get install yasm
  1. Pull the code from github and cd to the code directory
git clone https://github.com/Bilibili/ijkplayer.git ijkplayer-android
cd ijkplayer-android
  1. Update the code to the latest version. The latest version number can be found on GitHub. Enter the command:
git checkout -B latest k0.8.1.2
  1. Initialization, including pulling the ffmpeg code to the local operation, enter the command:
./init-android.sh
  1. Clean it and enter the command:
cd android/contrib

./compile-ffmpeg.sh clean
  1. Compile the ffmpeg soft decoding library and enter the command:
./compile-ffmpeg.sh all
  1. cd to the previous directory and enter the command:
cd ..
  1. Get the ijkplayer project and enter the command:
./compile-ijk.sh all

In fact, it is to follow the steps and type commands on the command line. Read the instructions clearly, don't make mistakes, you should be able to get our ijkplayer project soon. This project already supports all video formats.

In this project, there are many modules, such as exmaple, ijkplayer-java, ijkplayer-armv5, etc. They are examples, ijkplayer java layer code, native layer code adapted to different CPUs, etc.

Simple ijkplayer use

Below is the method I use. You can watch this demo directly: github address .

  1. Import dependencies.
    The support library of ijkplayer I use is compiled. So I depend on these modules
    compile project(':ijkplayer-java')
    compile project(':ijkplayer-armv5')
    compile project(':ijkplayer-armv7a')
    compile project(':ijkplayer-arm64')
    compile project(':ijkplayer-x86')
    compile project(':ijkplayer-x86_64')
  1. The custom playback control
    provided by ijkplayer does not provide a player control for us to use, so we define one ourselves. The main thing is to create a surfaceview and assign it to IMediaPlayer.

    /**
     * 由ijkplayer提供,用于播放视频,需要给他传入一个surfaceView
     */
    private IMediaPlayer mMediaPlayer = null;

    /**
     * 视频文件地址
     */
    private String mPath = "";

    private SurfaceView surfaceView;

    private VideoPlayerListener listener;
    private Context mContext;

    public VideoPlayerIJK(@NonNull Context context) {
        super(context);
        initVideoView(context);
    }

    public VideoPlayerIJK(@NonNull Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        initVideoView(context);
    }

    public VideoPlayerIJK(@NonNull Context context, @Nullable AttributeSet attrs, @AttrRes int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initVideoView(context);
    }

    private void initVideoView(Context context) {
        mContext = context;

        //获取焦点,不知道有没有必要~。~
        setFocusable(true);
    }

    /**
     * 设置视频地址。
     * 根据是否第一次播放视频,做不同的操作。
     *
     * @param path the path of the video.
     */
    public void setVideoPath(String path) {
        if (TextUtils.equals("", mPath)) {
            //如果是第一次播放视频,那就创建一个新的surfaceView
            mPath = path;
            createSurfaceView();
        } else {
            //否则就直接load
            mPath = path;
            load();
        }
    }

    /**
     * 新建一个surfaceview
     */
    private void createSurfaceView() {
        //生成一个新的surface view
        surfaceView = new SurfaceView(mContext);
        surfaceView.getHolder().addCallback(new LmnSurfaceCallback());
        LayoutParams layoutParams = new LayoutParams(LayoutParams.MATCH_PARENT
                , LayoutParams.MATCH_PARENT, Gravity.CENTER);
        surfaceView.setLayoutParams(layoutParams);
        this.addView(surfaceView);
    }

    /**
     * surfaceView的监听器
     */
    private class LmnSurfaceCallback implements SurfaceHolder.Callback {
        @Override
        public void surfaceCreated(SurfaceHolder holder) {
        }

        @Override
        public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
            //surfaceview创建成功后,加载视频
            load();
        }

        @Override
        public void surfaceDestroyed(SurfaceHolder holder) {
        }
    }

    /**
     * 加载视频
     */
    private void load() {
        //每次都要重新创建IMediaPlayer
        createPlayer();
        try {
            mMediaPlayer.setDataSource(mPath);
        } catch (IOException e) {
            e.printStackTrace();
        }
        //给mediaPlayer设置视图
        mMediaPlayer.setDisplay(surfaceView.getHolder());

        mMediaPlayer.prepareAsync();
    }

    /**
     * 创建一个新的player
     */
    private void createPlayer() {
        if (mMediaPlayer != null) {
            mMediaPlayer.stop();
            mMediaPlayer.setDisplay(null);
            mMediaPlayer.release();
        }
        IjkMediaPlayer ijkMediaPlayer = new IjkMediaPlayer();
        ijkMediaPlayer.native_setLogLevel(IjkMediaPlayer.IJK_LOG_DEBUG);

//开启硬解码        ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec", 1);

        mMediaPlayer = ijkMediaPlayer;

        if (listener != null) {
            mMediaPlayer.setOnPreparedListener(listener);
            mMediaPlayer.setOnInfoListener(listener);
            mMediaPlayer.setOnSeekCompleteListener(listener);
            mMediaPlayer.setOnBufferingUpdateListener(listener);
            mMediaPlayer.setOnErrorListener(listener);
        }
    }


    public void setListener(VideoPlayerListener listener) {
        this.listener = listener;
        if (mMediaPlayer != null) {
            mMediaPlayer.setOnPreparedListener(listener);
        }
    }

    /**
     * -------======--------- 下面封装了一下控制视频的方法
     */

    public void start() {
        if (mMediaPlayer != null) {
            mMediaPlayer.start();
        }
    }

    public void release() {
        if (mMediaPlayer != null) {
            mMediaPlayer.reset();
            mMediaPlayer.release();
            mMediaPlayer = null;
        }
    }

    public void pause() {
        if (mMediaPlayer != null) {
            mMediaPlayer.pause();
        }
    }

    public void stop() {
        if (mMediaPlayer != null) {
            mMediaPlayer.stop();
        }
    }


    public void reset() {
        if (mMediaPlayer != null) {
            mMediaPlayer.reset();
        }
    }


    public long getDuration() {
        if (mMediaPlayer != null) {
            return mMediaPlayer.getDuration();
        } else {
            return 0;
        }
    }


    public long getCurrentPosition() {
        if (mMediaPlayer != null) {
            return mMediaPlayer.getCurrentPosition();
        } else {
            return 0;
        }
    }


    public void seekTo(long l) {
        if (mMediaPlayer != null) {
            mMediaPlayer.seekTo(l);
        }
    }
}

Our control inherits from framelayout. This control is responsible for storing a surfaceView and an IMediaPlayer.

3. Set up the listener. I define a listener myself, which inherits the n listeners of IMediaPlayer.

public abstract class VideoPlayerListener implements IMediaPlayer.OnBufferingUpdateListener, IMediaPlayer.OnCompletionListener, IMediaPlayer.OnPreparedListener, IMediaPlayer.OnInfoListener, IMediaPlayer.OnVideoSizeChangedListener, IMediaPlayer.OnErrorListener, IMediaPlayer.OnSeekCompleteListener {
}

When we inherit this abstract class, the most important thing is to start the video in the onPrepared() method:

ijkPlayer.setListener(new VideoPlayerListener() {
            @Override
            public void onBufferingUpdate(IMediaPlayer mp, int percent) {
            }

            @Override
            public void onCompletion(IMediaPlayer mp) {
            }

            @Override
            public boolean onError(IMediaPlayer mp, int what, int extra) {
                return false;
            }

            @Override
            public boolean onInfo(IMediaPlayer mp, int what, int extra) {
                return false;
            }

            @Override
            public void onPrepared(IMediaPlayer mp) {
                // 视频准备好播放了,但是他不会自动播放,需要手动让他开始。
                mp.start();
            }

            @Override
            public void onSeekComplete(IMediaPlayer mp) {

            }

            @Override
            public void onVideoSizeChanged(IMediaPlayer mp, int width, int height, int sar_num, int sar_den) {
                //在此可以获取到视频的宽和高
            }
        });
  1. put player controls in xml
  2. Load the so package in the activity, set the listener, and set the path
 //加载native库
try {
    IjkMediaPlayer.loadLibrariesOnce(null);
    IjkMediaPlayer.native_profileBegin("libijkplayer.so");
 } catch (Exception e) {
    this.finish();
 }
ijkPlayer.setListener(...)
ijkPlayer.setVideoPath(path);

Remember to turn off the native library in the onStop() method

IjkMediaPlayer.native_profileEnd();

This completes a simple video player.

Precautions

1. It is not recommended to reassign the setDataSource of IjkMediaPlayer. Every time you change the video source, you need to: player.release() -> create new player -> player.setDataSource.

But SurfaceView doesn't need to be recreated every time.

2. Set double speed playback: IjkMediaPlayer.setSpeed();

3. Some important video information return codes (these information return codes can be obtained from the listener's onInfo() method):

int MEDIA_INFO_VIDEO_RENDERING_START = 3;//视频准备渲染
int MEDIA_INFO_BUFFERING_START = 701;//开始缓冲
int MEDIA_INFO_BUFFERING_END = 702;//缓冲结束
int MEDIA_INFO_VIDEO_ROTATION_CHANGED = 10001;//视频选择信息
int MEDIA_ERROR_SERVER_DIED = 100;//视频中断,一般是视频源异常或者不支持的视频类型。
int MEDIA_ERROR_IJK_PLAYER = -10000,//一般是视频源有问题或者数据格式不支持,比如音频不是AAC之类的
int MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK = 200;//数据错误没有有效的回收

References:

Compile ijkplayer-android under ubuntu








Live video technology (4): use Ijkplayer to play live video

1. Ijkplayer encoding

IjkPlayer supports hard decoding and soft decoding. The video angle will not be rotated during soft decoding. At this time, you need to obtain onInfothe what == IMediaPlayer.MEDIA_INFO_VIDEO_ROTATION_CHANGEDangle and rotate the picture yourself. Or turn on hard decoding and hard decoding, but hard decoding can easily cause black screen and silence (hardware compatibility problem). The following is the code related to setting hard decoding

mediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec", 1);
mediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-auto-rotate", 1);
mediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-handle-resolution-change", 1);

2. Ijkplayer double-speed playback

At present, the latest version of IjkPlayer supports double-speed playback (version number 0.7.7.1) . In earlier versions, the support for double-speed playback is not very good, and only mobile phones with 6.0 and above can perform double-speed playback. But at present, although all models support double speed, the phone below 6.0 obviously has a tone change, so the user experience is not very good (the article on tone change will be updated in the future). Here is the latest speed setting code:

public void setSpeed(float speed) {
    _setPropertyFloat(FFP_PROP_FLOAT_PLAYBACK_RATE, speed);
}

Old version code:

@TargetApi(Build.VERSION_CODES.M)
    public void setSpeed(float speed) {
        _setPropertyFloat(FFP_PROP_FLOAT_PLAYBACK_RATE, speed);
    }

The old version of the code can only set the speed, but can't get it, so basically it can't be used (and the model only supports models above 6.0).

3. Some important video return codes of Ijkplayer

复制代码
int MEDIA_INFO_VIDEO_RENDERING_START = 3;//视频准备渲染
int MEDIA_INFO_BUFFERING_START = 701;//开始缓冲
int MEDIA_INFO_BUFFERING_END = 702;//缓冲结束
int MEDIA_INFO_VIDEO_ROTATION_CHANGED = 10001;//视频选择信息
int MEDIA_ERROR_SERVER_DIED = 100;//视频中断,一般是视频源异常或者不支持的视频类型。
int MEDIA_ERROR_IJK_PLAYER = -10000,//一般是视频源有问题或者数据格式不支持,比如音频不是AAC之类的
int MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK = 200;//数据错误没有有效的回收
复制代码

4、Ijkplayer调用seekTo存在的问题

部分视频播放时,调用seekTo的时候,会跳回到拖动前的位置,这是因为视频的关键帧的问题(GOP导致的),视频压缩比较高,而seek只支持关键帧,出现这个情况就是原始的视频文件中i帧比较少,播放器会在拖动的位置找最近的关键帧。所以,目前针对此问题IjkPlayer无解。

5. Ijkplayer音视频不同步问题

在开始使用过程当中对rtmp视频流进行播放,会出现严重的视频音频不同步现象,并且随着播放的时间越长,视频与音频的差距越大。具体原因是CPU在处理视频帧的时候处理得太慢,默认的音视频同步方案是视频同步到音频, 导致了音频播放过快,视频跟不上。

{ "framedrop",                      "drop frames when cpu is too slow",
        OPTION_OFFSET(framedrop),       OPTION_INT(0, -1, 120) },

framedrop 控制着允许丢帧的范围。可以通过修改 framedrop 的数值来解决不同步的问题,framedrop 是在视频帧处理不过来的时候丢弃一些帧达到同步的效果。具体设置:

player.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "framedrop", 5);

framedrop 的具体大小根据实际情况而定, 一般丢太多帧也不好,会影响用户的观看体验。

6、如何支持https链接播放?

如果你的项目要进行加密播放HLS协议的视频,要想支持https,须要在普通编译的基础上,进行一些配置。

接下来我们来编译openssl

a). init openssl

$ cd .. 进入到ijkplayer的目下
$ ./init-android-openssl.sh 去远程仓库拉取openssl的远程代码,如果是iOS的,这里是init-ios-openssl.h 

b). compile openssl

$ cd android/contrib
$ ./compile-openssl.sh clean
$ ./compile-openssl.sh all

经过以上步骤已经编译好openssl了,然后我们执行一下方法

$./compile-ffmpeg.sh clean
编译ffmpeg软解码库,这个过程会生成各种架构的ffmpeg 这个过程比较耗时
$./compile-ffmpeg.sh all

7、Ijkplayer使用小技巧

a. 下载速度可以通过IjkMediaPlayer的 getTcpSpeed获取。

b. 高分辨率开启硬解码,不支持的话会自动切换到软解,就算开启mediacodec,如果设备不支持,显示的解码器也是avcodec软解。

c. IjkMediaPlayer.setOption可配置的对应头文件参考:ff_ffplay_options

d. 设置cookie 可以通过ijkPlayer的public void setDataSource(String path, Map<String, String> headers) 的header实现设置,参考ijkPlayer的issues-1150,headers也是在内部被转化为何issuses一样的setOption方法




Android_观看视频卡顿原因及解决办法

在LiveView界面观看视频时,有时会出现画面卡顿的情况,有一种情况可能是只解了I帧的原因。在调用startShow(int avChannel,  boolean clearBuf, 

 boolean runSoftwareDecode,  boolean isDropFrame)方法时将isDropFrame设置成了true。

解决方法:将isDropFrame设置为false即可。



Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325747996&siteId=291194637