整理完vlc后,随手整理了下ijkPlayer;
- ijkplayer是Bilibili公司开源的播放器实现,整合了FFMpeg, ExoPlayer,MediaPlayer等多种实现,提供了类似于MediaPlayer的API,可以实现软硬解码自由切换,自定义TextureView实现,同时得益于FFMpeg的能力,也能支持多种流媒体协议(RTSP,RTMP,HLS等),多种视频编码格式(h264,
mpeg4, mjpeg),具有很高的灵活性,可以定制实现自己特色的播放器(比如支持视频缩放,视频翻转等)。 - IJKPlayer源码地址:
一、ubuntu下面编译环境搭建;
例如我阿里云服务端 ubuntu 16.04下载ndk
1、下载Android NDK自解压包:
wget -c http://dl.google.com/android/ndk/android-ndk-r18b-linux-x86_64.bin
android-ndk-r21b-linux-x86_64.bin
2、将Android NDK压缩包解压到你想要的目录下,如
/opt/android/ndk/
chmod a+x android-ndk-r18b-linux-x86_64.bin
./android-ndk-r18b-linux-x86_64.bin
解压后目录结构为:
/opt/android/ndk/android-ndk-r18b
3、配置PATH路径:
命令:
$ sudo vim /etc/profile,在文件末尾加入如下内容:
#set NDK env
export ANDROID_NDK=/opt/android/ndk/android-ndk-r14b
export NDK_HOME=/opt/android/ndk/android-ndk-r14b
export PATH=$NDK_HOME:$PATH
同理SDK也需要安装:
wget https://dl.google.com/android/android-sdk_r24.4.1-linux.tgz?utm_source=androiddevtools&utm_medium=website
mv android-sdk_r24.4.1-linux.tgz\?utm_source\=androiddevtools android-sdk_r24.4.1-linux.tgz
tar zxvf android-sdk_r24.4.1-linux.tgz
export ANDROID_SDK_HOME=/opt/android/sdk/android-sdk-linux
export PATH=$PATH:${ANDROID_SDK_HOME}/tools
export PATH=$PATH:${ANDROID_SDK_HOME}/platform-tools
- 下载对应gradle版本
https://gradle.org/releases/
export GRADLE_HOME=/opt/android/gradle/gradle-2.14.1
export PATH=$GRADLE_HOME/bin:$PATH
source /etc/profile
二、下载源码并且切换到对应稳定版本分支;
git clone https://github.com/Bilibili/ijkplayer.git ijkplayer-android
cd ijkplayer-android
git checkout -B latest k0.8.8
./init-android.sh
- 下载ffmpeg相关code;
- 编译ffmpeg
cd android/contrib
./compile-ffmpeg.sh clean
./compile-ffmpeg.sh all
- 编译ijkPlayer相关库:
cd ..
./compile-ijk.sh all
- 编译ijkplayer apk:
./gradlew clean build
成功编译
需要注意的是,如果在ubuntu下面编译建议修改各个项目的build.gradle;
diff --git a/android/ijkplayer/gradle/wrapper/gradle-wrapper.properties b/android/ijkplayer/gradle/wrapper/gradle-wrapper.properties
index f47ae66..da11b25 100644
--- a/android/ijkplayer/gradle/wrapper/gradle-wrapper.properties
+++ b/android/ijkplayer/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-all.zip
+distributionUrl=gradle-2.14.1-all.zip
diff --git a/android/ijkplayer/ijkplayer-arm64/build.gradle b/android/ijkplayer/ijkplayer-arm64/build.gradle
index a8cbe0c..17f01cf 100644
--- a/android/ijkplayer/ijkplayer-arm64/build.gradle
+++ b/android/ijkplayer/ijkplayer-arm64/build.gradle
@@ -6,7 +6,9 @@ android {
compileSdkVersion rootProject.ext.compileSdkVersion
//noinspection GroovyAssignabilityCheck
buildToolsVersion rootProject.ext.buildToolsVersion
-
+ lintOptions {
+ abortOnError false
+ }
defaultConfig {
minSdkVersion 21
targetSdkVersion rootProject.ext.targetSdkVersion
diff --git a/android/ijkplayer/ijkplayer-armv5/build.gradle b/android/ijkplayer/ijkplayer-armv5/build.gradle
index b3b7b3a..8c1c8ce 100644
--- a/android/ijkplayer/ijkplayer-armv5/build.gradle
+++ b/android/ijkplayer/ijkplayer-armv5/build.gradle
@@ -6,6 +6,9 @@ android {
compileSdkVersion rootProject.ext.compileSdkVersion
//noinspection GroovyAssignabilityCheck
buildToolsVersion rootProject.ext.buildToolsVersion
+ lintOptions {
+ abortOnError false
+ }
defaultConfig {
minSdkVersion 9
diff --git a/android/ijkplayer/ijkplayer-armv7a/build.gradle b/android/ijkplayer/ijkplayer-armv7a/build.gradle
index b3b7b3a..8c1c8ce 100644
--- a/android/ijkplayer/ijkplayer-armv7a/build.gradle
+++ b/android/ijkplayer/ijkplayer-armv7a/build.gradle
@@ -6,6 +6,9 @@ android {
compileSdkVersion rootProject.ext.compileSdkVersion
//noinspection GroovyAssignabilityCheck
buildToolsVersion rootProject.ext.buildToolsVersion
+ lintOptions {
+ abortOnError false
+ }
defaultConfig {
minSdkVersion 9
diff --git a/android/ijkplayer/ijkplayer-java/build.gradle b/android/ijkplayer/ijkplayer-java/build.gradle
index 30d3ad2..9797cdd 100644
--- a/android/ijkplayer/ijkplayer-java/build.gradle
+++ b/android/ijkplayer/ijkplayer-java/build.gradle
@@ -20,6 +20,8 @@ android {
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
+
+
}
dependencies {
diff --git a/android/ijkplayer/ijkplayer-x86/build.gradle b/android/ijkplayer/ijkplayer-x86/build.gradle
index b3b7b3a..3abae56 100644
--- a/android/ijkplayer/ijkplayer-x86/build.gradle
+++ b/android/ijkplayer/ijkplayer-x86/build.gradle
@@ -6,6 +6,11 @@ android {
compileSdkVersion rootProject.ext.compileSdkVersion
//noinspection GroovyAssignabilityCheck
buildToolsVersion rootProject.ext.buildToolsVersion
+ android {
+ lintOptions {
+ abortOnError false
+ }
+}
defaultConfig {
minSdkVersion 9
diff --git a/android/ijkplayer/ijkplayer-x86_64/build.gradle b/android/ijkplayer/ijkplayer-x86_64/build.gradle
index a8cbe0c..eb6d58a 100644
--- a/android/ijkplayer/ijkplayer-x86_64/build.gradle
+++ b/android/ijkplayer/ijkplayer-x86_64/build.gradle
@@ -6,6 +6,11 @@ android {
compileSdkVersion rootProject.ext.compileSdkVersion
//noinspection GroovyAssignabilityCheck
buildToolsVersion rootProject.ext.buildToolsVersion
+ android {
+ lintOptions {
+ abortOnError false
+ }
+}
defaultConfig {
minSdkVersion 21
全项目编译容易引起
- 修改后编译成功
三、集成ffmpeg 相关库以及ijk jni aar到android studio个人项目;
copy生成的aar以及对应的平台so到android Studio工程
修改APP下的build.gradle, 主要设置.so及.aar的位置
模块 | 简要介绍 |
---|---|
ijkplayer-java | ijkplayer的一些操作封装及定义。这里面是通用的API接口,里面最主要的是IMediaPlayer,它是用来渲染显示多媒体的 |
ijkplayer-exo | google开源的一个新的播放器ExoPlayer,在Demo中和ijkplayer对比用的。通过安装ijkplayer可以发现setting里面可以选择不同player来渲染多媒体显示,该模块下面就是一个MediaPlayer |
ijkplayer-example | 测试程序 |
ijkplayer-{arch} | 编译出来的各个版本的.so文件 |
主要实现类:
package com.example.ijkplayer.view;
import android.content.Context;
import android.graphics.Color;
import android.media.AudioManager;
import android.net.Uri;
import android.util.AttributeSet;
import android.util.Log;
import android.view.Gravity;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.widget.FrameLayout;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.example.ijkplayer.VideoListener;
import java.io.IOException;
import java.util.Map;
import tv.danmaku.ijk.media.player.IMediaPlayer;
import tv.danmaku.ijk.media.player.IjkMediaPlayer;
public class PlayerView extends FrameLayout {
private IMediaPlayer mMediaPlayer = null;
private String mPath;
private Map<String, String> mHeader;
private SurfaceView mSurfaceView;
private Context mContext;
private boolean mEnableMediaCodec;
private AudioManager mAudioManager;
private AudioFocusHelper mAudioFocusHelper;
private VideoListener mListener;
public PlayerView(@NonNull Context context) {
this(context, null);
}
public PlayerView(@NonNull Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public PlayerView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
/**
* 初始化
*/
private void init(Context context) {
mContext = context;
setBackgroundColor(Color.BLACK);
createSurfaceView();
mAudioManager = (AudioManager) mContext.getApplicationContext().getSystemService(Context.AUDIO_SERVICE);
mAudioFocusHelper = new AudioFocusHelper();
}
/**
* 创建surfaceView
*/
private void createSurfaceView() {
mSurfaceView = new SurfaceView(mContext);
mSurfaceView.getHolder().addCallback(new SurfaceHolder.Callback() {
@Override
public void surfaceCreated(SurfaceHolder surfaceHolder) {
}
@Override
public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i1, int i2) {
if (mMediaPlayer != null) {
mMediaPlayer.setDisplay(surfaceHolder);
}
}
@Override
public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
}
});
LayoutParams layoutParams = new LayoutParams(LayoutParams.MATCH_PARENT
, LayoutParams.MATCH_PARENT, Gravity.CENTER);
// mSurfaceView.setLayoutParams(layoutParams);
addView(mSurfaceView, 0, layoutParams);
}
/**
* 创建player
*/
private IMediaPlayer createPlayer() {
IjkMediaPlayer ijkMediaPlayer = new IjkMediaPlayer();
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "opensles", 1);
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "overlay-format", IjkMediaPlayer.SDL_FCC_RV32);
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "framedrop", 1);
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "start-on-prepared", 0);
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "http-detect-range-support", 1);
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_CODEC, "skip_loop_filter", 48);
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_CODEC, "min-frames", 100);
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "enable-accurate-seek", 1);
ijkMediaPlayer.setVolume(0.8f, 0.8f);
setEnableMediaCodec(ijkMediaPlayer, mEnableMediaCodec);
return ijkMediaPlayer;
}
private void setEnableMediaCodec(IjkMediaPlayer ijkMediaPlayer, boolean isEnable) {
int value = isEnable ? 1 : 0;
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec", value);//开启硬解码
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-auto-rotate", value);
ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-handle-resolution-change", value);
}
public void setEnableMediaCodec(boolean isEnable) {
mEnableMediaCodec = isEnable;
}
private void setListener(IMediaPlayer player) {
player.setOnPreparedListener(mPreparedListener);
player.setOnVideoSizeChangedListener(mVideoSizeChangedListener);
}
public void setPath(String path) {
setPath(path, null);
}
public void setPath(String path, Map<String, String> header) {
mPath = path;
mHeader = header;
}
/**
* 加载视频
*/
public void load() throws IOException {
if (mMediaPlayer != null) {
mMediaPlayer.pause();
mMediaPlayer.stop();
mMediaPlayer.release();
}
mMediaPlayer = createPlayer();
setListener(mMediaPlayer);
mMediaPlayer.setDisplay(mSurfaceView.getHolder());
mMediaPlayer.setDataSource(mContext, Uri.parse(mPath), mHeader);
mMediaPlayer.prepareAsync();
}
/**
* 设置自己的player回调
*/
public void setVideoListener(VideoListener listener) {
mListener = listener;
}
private IMediaPlayer.OnPreparedListener mPreparedListener = new IMediaPlayer.OnPreparedListener() {
@Override
public void onPrepared(IMediaPlayer iMediaPlayer) {
if (mListener != null) {
mListener.onPrepared(iMediaPlayer);
}
}
};
public void start() {
if (mMediaPlayer != null) {
mMediaPlayer.start();
Log.d("Mr.xw==", "start_player");
mAudioFocusHelper.requestFocus();
}
}
public void release() {
if (mMediaPlayer != null) {
mMediaPlayer.reset();
mMediaPlayer.release();
mMediaPlayer = null;
mAudioFocusHelper.abandonFocus();
}
}
public void pause() {
if (mMediaPlayer != null) {
mMediaPlayer.pause();
mAudioFocusHelper.abandonFocus();
}
}
public void stop() {
if (mMediaPlayer != null) {
mMediaPlayer.stop();
mAudioFocusHelper.abandonFocus();
}
}
public void reset() {
if (mMediaPlayer != null) {
mMediaPlayer.reset();
mAudioFocusHelper.abandonFocus();
}
}
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);
}
}
public boolean isPlaying() {
if (mMediaPlayer != null) {
return mMediaPlayer.isPlaying();
}
return false;
}
private IMediaPlayer.OnVideoSizeChangedListener mVideoSizeChangedListener = new IMediaPlayer.OnVideoSizeChangedListener() {
@Override
public void onVideoSizeChanged(IMediaPlayer iMediaPlayer, int i, int i1, int i2, int i3) {
int videoWidth = iMediaPlayer.getVideoWidth();
int videoHeight = iMediaPlayer.getVideoHeight();
if (videoWidth != 0 && videoHeight != 0) {
mSurfaceView.getHolder().setFixedSize(videoWidth, videoHeight);
}
}
};
/**
* 音频焦点改变监听
*/
private class AudioFocusHelper implements AudioManager.OnAudioFocusChangeListener {
boolean startRequested = false;
boolean pausedForLoss = false;
int currentFocus = 0;
@Override
public void onAudioFocusChange(int focusChange) {
if (currentFocus == focusChange) {
return;
}
currentFocus = focusChange;
switch (focusChange) {
case AudioManager.AUDIOFOCUS_GAIN://获得焦点
case AudioManager.AUDIOFOCUS_GAIN_TRANSIENT://暂时获得焦点
if (startRequested || pausedForLoss) {
start();
startRequested = false;
pausedForLoss = false;
}
if (mMediaPlayer != null)//恢复音量
mMediaPlayer.setVolume(1.0f, 1.0f);
break;
case AudioManager.AUDIOFOCUS_LOSS://焦点丢失
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT://焦点暂时丢失
if (isPlaying()) {
pausedForLoss = true;
pause();
}
break;
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK://此时需降低音量
if (mMediaPlayer != null && isPlaying()) {
mMediaPlayer.setVolume(0.1f, 0.1f);
}
break;
}
}
boolean requestFocus() {
if (currentFocus == AudioManager.AUDIOFOCUS_GAIN) {
return true;
}
if (mAudioManager == null) {
return false;
}
int status = mAudioManager.requestAudioFocus(this, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN);
if (AudioManager.AUDIOFOCUS_REQUEST_GRANTED == status) {
currentFocus = AudioManager.AUDIOFOCUS_GAIN;
return true;
}
startRequested = true;
return false;
}
boolean abandonFocus() {
if (mAudioManager == null) {
return false;
}
startRequested = false;
int status = mAudioManager.abandonAudioFocus(this);
return AudioManager.AUDIOFOCUS_REQUEST_GRANTED == status;
}
}
}
package com.example.ijkplayer;
import tv.danmaku.ijk.media.player.IMediaPlayer;
public interface VideoListener extends IMediaPlayer.OnBufferingUpdateListener, IMediaPlayer.OnCompletionListener,
IMediaPlayer.OnPreparedListener, IMediaPlayer.OnInfoListener, IMediaPlayer.OnVideoSizeChangedListener,
IMediaPlayer.OnErrorListener, IMediaPlayer.OnSeekCompleteListener {
}
package com.example.ijkplayer.activity;
import android.app.Activity;
import android.os.Bundle;
import android.widget.Toast;
import com.example.ijkplayer.VideoListener;
import com.example.ijkplayer.view.PlayerView;
import com.mgtv.exodemo.R;
import java.io.IOException;
import tv.danmaku.ijk.media.player.IMediaPlayer;
public class IJKPlayerActivity extends Activity implements VideoListener {
private PlayerView mMediaPlayer = null;
private String videoUrlRtp = "rtp://239.76.245.115:1234";
private String videoUrlHls = "http://10.255.30.137:8082/EDS/RedirectPlay/000000000000/vod/f95e41c67ca2410c89b335ee5f5eecb8/3cc6fae6d07740aa8d57934e26cc2632?UserToken=123456789&UserName=6830018";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.ijkplayer);
mMediaPlayer = findViewById(R.id.ijk_player);
}
@Override
protected void onResume() {
super.onResume();
initIJKPlayer();
}
@Override
protected void onPause() {
super.onPause();
releaseData();
}
@Override
protected void onStop() {
super.onStop();
}
@Override
protected void onDestroy() {
super.onDestroy();
releaseData();
}
private void releaseData() {
if (isFinishing()) {
if (mMediaPlayer != null) {
mMediaPlayer.stop();
mMediaPlayer.release();
mMediaPlayer = null;
}
}
}
/**
* 初始化播放器
*/
private void initIJKPlayer() {
mMediaPlayer.setVideoListener(this);
mMediaPlayer.setPath(videoUrlRtp);
/**false 软解,true:硬解 默认使用硬解码*/
mMediaPlayer.setEnableMediaCodec(true);
try {
mMediaPlayer.load();
} catch (IOException e) {
Toast.makeText(this, "播放失败", Toast.LENGTH_SHORT);
e.printStackTrace();
}
}
@Override
public void onPointerCaptureChanged(boolean hasCapture) {
}
@Override
public void onBufferingUpdate(IMediaPlayer iMediaPlayer, int i) {
}
@Override
public void onCompletion(IMediaPlayer iMediaPlayer) {
}
@Override
public boolean onError(IMediaPlayer iMediaPlayer, int i, int i1) {
return false;
}
@Override
public boolean onInfo(IMediaPlayer iMediaPlayer, int i, int i1) {
return false;
}
@Override
public void onPrepared(IMediaPlayer iMediaPlayer) {
mMediaPlayer.start();
}
@Override
public void onSeekComplete(IMediaPlayer iMediaPlayer) {
}
@Override
public void onVideoSizeChanged(IMediaPlayer iMediaPlayer, int i, int i1, int i2, int i3) {
}
}
/**false 软解,true:硬解*/
mMediaPlayer.setEnableMediaCodec(false);
硬解的时候可以看到cpu占用率
软解的时候可以看到cpu占用率:
如果需要播放直播rtp的话,需要使能rtp:
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --disable-protocol=rtp"
修改为以下,就可以打开rtsp协议了
export COMMON_FF_CFG_FLAGS="$COMMON_FF_CFG_FLAGS --enable-protocol=rtp"
执行以下命令,连接配置文件,开始编译
cd config
rm module.sh
ln -s module-lite.sh module.sh
cd android
cd contrib
./compile-ffmpeg.sh clean
./compile-ffmpeg.sh al
重新编译ffmpeg:
update ffmpge库
更新库后可以支持组播rtp(直播流)的播放;
四、IjkPlayer的常用Option设置
参数设置 | 功能说明 |
---|---|
1.设置是否开启变调 | mediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER,“soundtouch”,isModifyTone?0:1) |
2.设置是否开启环路过滤: 0开启,画面质量高,解码开销大,48关闭,画面质量差点,解码开销小 | mediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_CODEC,“skip_loop_filter”,isSkipLoopFilter?0:48L) |
3.设置播放前的最大探测时间 | mediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT,“analyzemaxduration”,100L) |
4.设置播放前的探测时间 1,达到首屏秒开效果 | mediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER,“soundtouch”,isModifyTone?0:1) |
5.播放前的探测Size,默认是1M, 改小一点会出画面更快 | mediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT,“probesize”,1024*10) |
6.每处理一个packet之后刷新io上下文 | mediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_FORMAT,“flush_packets”,1L) |
7. 是否开启预缓冲,一般直播项目会开启,达到秒开的效果,不过带来了播放丢帧卡顿的体验 | mediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER,“packet-buffering”,isBufferCache?1:0) |
8.播放重连次数 | mediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER,“reconnect”,5) |
9.最大缓冲大小,单位kb | mediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER,“max-buffer-size”,maxCacheSize)) |
10.跳帧处理,放CPU处理较慢时,进行跳帧处理,保证播放流程,画面和声音同步 | mediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER,“framedrop”,5) |
11.最大fps | mediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER,“max-fps”,30); |
12.设置硬解码方式 | 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) |
13.SeekTo设置优化 | mediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, “enable-accurate-seek”, 1); |
附上个人编译的支持rtp的相关库以及官方demo
https://download.csdn.net/download/xiaweilihai/12634232
个人测试demo需要在IPTV专网环境才能播放rtp,gitHub demo上修改播放Uri即可: