最近在做 m3u8网络视频播放,踩了不少坑,也试了不少的 框架,特别记录一下其中用的比较多的三种

第一种:media:ijkplayer

media:ijkplayer 是由 bilbil 提供的开源的视频 框架,但是由过之后感觉不太好用:

优点:
1、支持 Android 和 IOS
2、支持多种视频的硬解码

缺点:
1、加载时间过长;从开始加载 到 开始播放 第一帧视频,中间最少需要十秒时间(一开始以为是自己的配置有问题,但在网上找了一下,但都没有找到好的解决方案)
2、不支持实时视频截图(由于项目的需要,需要关闭时,最后显示的视频截图,但看了一下源码,并没有找到提供相关源码)

如果有哪位大神解决的,麻烦给留个言!!!!!

media:ijkplayer 的github地址:https://github.com/bilibili/ijkplayer

集成方式:

// required, enough for most devices.implementation 'tv.danmaku.ijk.media:ijkplayer-java:0.8.8'implementation 'tv.danmaku.ijk.media:ijkplayer-armv7a:0.8.8'// Other ABIs: optionalimplementation 'tv.danmaku.ijk.media:ijkplayer-armv5:0.8.8'implementation 'tv.danmaku.ijk.media:ijkplayer-arm64:0.8.8'implementation 'tv.danmaku.ijk.media:ijkplayer-x86:0.8.8'implementation 'tv.danmaku.ijk.media:ijkplayer-x86_64:0.8.8'// ExoPlayer as IMediaPlayer: optional, experimentalimplementation 'tv.danmaku.ijk.media:ijkplayer-exo:0.8.8'

混淆配置:

-keep class tv.danmaku.ijk.media.player.** { *; }

使用:
1):IjkVideoView

public class IjkVideoView extends FrameLayout {private Context mContext;//上下文private IMediaPlayer mMediaPlayer = null;//视频控制类private VideoPlayerListener mVideoPlayerListener;//自定义监听器private SurfaceView mSurfaceView;//播放视图private String mPath = "";//视频文件地址public IjkVideoView(@NonNull Context context) {super(context);initVideoView(context);}public IjkVideoView(@NonNull Context context, @Nullable AttributeSet attrs) {super(context, attrs);initVideoView(context);}public IjkVideoView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);initVideoView(context);}@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)public IjkVideoView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {super(context, attrs, defStyleAttr, defStyleRes);}public abstract static class VideoPlayerListener implements IMediaPlayer.OnPreparedListener,IMediaPlayer.OnCompletionListener,IMediaPlayer.OnErrorListener {}private void initVideoView(Context context) {mContext = context;setFocusable(true);}public void setPath(String path) {if (TextUtils.equals("", mPath)) {mPath = path;initSurfaceView();} else {mPath = path;loadVideo();}}private void initSurfaceView() {mSurfaceView = new SurfaceView(mContext);mSurfaceView.getHolder().addCallback(new LmnSurfaceCallback());LayoutParams layoutParams = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT, Gravity.CENTER);mSurfaceView.setLayoutParams(layoutParams);this.addView(mSurfaceView);}//surfaceView的监听器private class LmnSurfaceCallback implements SurfaceHolder.Callback {@Overridepublic void surfaceCreated(SurfaceHolder holder) {}@Overridepublic void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {loadVideo();}@Overridepublic void surfaceDestroyed(SurfaceHolder holder) {}}//加载视频private void loadVideo() {if (mMediaPlayer != null) {mMediaPlayer.stop();mMediaPlayer.release();}IjkMediaPlayer ijkMediaPlayer = new IjkMediaPlayer();mMediaPlayer = ijkMediaPlayer;if (mVideoPlayerListener != null) {mMediaPlayer.setOnPreparedListener(mVideoPlayerListener);mMediaPlayer.setOnErrorListener(mVideoPlayerListener);}try {mMediaPlayer.setDataSource(mPath);} catch (IOException e) {e.printStackTrace();}mMediaPlayer.setDisplay(mSurfaceView.getHolder());mMediaPlayer.prepareAsync();}public void setListener(VideoPlayerListener listener) {this.mVideoPlayerListener = listener;if (mMediaPlayer != null) {mMediaPlayer.setOnPreparedListener(listener);}}public boolean isPlaying() {if (mMediaPlayer != null) {return mMediaPlayer.isPlaying();}return false;}public void start() {if (mMediaPlayer != null) {mMediaPlayer.start();}}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 void release() {if (mMediaPlayer != null) {mMediaPlayer.reset();mMediaPlayer.release();mMediaPlayer = null;}}
}

2):布局

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:background="@color/color_000000"android:layout_height="match_parent"><LinearLayoutandroid:layout_width="match_parent"android:layout_centerInParent="true"android:layout_height="wrap_content"android:background="@color/color_000000"android:orientation="vertical"><com.tzyun.pip.view.IjkVideoViewandroid:id="@+id/jkVideoView"android:layout_width="match_parent"android:layout_height="300dp" /></LinearLayout><ImageViewandroid:id="@+id/back"android:layout_width="wrap_content"android:layout_height="48dp"android:contentDescription="@null"android:paddingStart="13dp"android:paddingEnd="13dp"android:src="@mipmap/back_video" /><ProgressBarandroid:id="@+id/progressBar"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_centerInParent="true" /></RelativeLayout>

3):Activity

public class IjkPlayerVideoActivity extends Activity {private AlertDialog alertDialog;private String path;private IjkVideoView ijkVideoView;@Overridepublic void onCreate(Bundle icicle) {super.onCreate(icicle);requestWindowFeature(Window.FEATURE_NO_TITLE);getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);path = getIntent().getStringExtra("ADDRESS");setContentView(R.layout.ijkplayervideo_layout);findViewById(R.id.back).setOnClickListener(v -> onBackPressed());AlertDialog.Builder builder = new AlertDialog.Builder(this);builder.setTitle("播放错误");builder.setNegativeButton("重试", (dialogInterface, i) -> setLiveParam());builder.setPositiveButton("退出", (dialogInterface, i) -> finish());alertDialog = builder.create();alertDialog.setCancelable(false);alertDialog.setCanceledOnTouchOutside(false);setLiveParam();}private void setLiveParam() {findViewById(R.id.progressBar).setVisibility(View.VISIBLE);IjkMediaPlayer.loadLibrariesOnce(null);IjkMediaPlayer.native_profileBegin("libijkplayer.so");//监听ijkVideoView=findViewById(R.id.jkVideoView);ijkVideoView.setListener(new IjkVideoView.VideoPlayerListener() {@Overridepublic void onPrepared(IMediaPlayer mp) {//播放成功处理mp.start();findViewById(R.id.progressBar).setVisibility(View.INVISIBLE);}@Overridepublic void onCompletion(IMediaPlayer iMediaPlayer) {}@Overridepublic boolean onError(IMediaPlayer mp, int what, int extra) {ToastUtils.showToast("播放失败");finish();return true;}});//        path = "你自己的播放地址";//路径ijkVideoView.setPath(path);ijkVideoView.start();}@Overridepublic void onConfigurationChanged(@NonNull Configuration newConfig) {super.onConfigurationChanged(newConfig);LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) ijkVideoView.getLayoutParams();//横屏if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {params.height = RelativeLayout.LayoutParams.MATCH_PARENT;} else {//竖屏params.height = (int) UnitUtils.dp2px(this, 300);}ijkVideoView.setLayoutParams(params);}@Overrideprotected void onDestroy() {IjkMediaPlayer.native_profileEnd();if (alertDialog != null && alertDialog.isShowing()) {alertDialog.dismiss();}ijkVideoView.stop();super.onDestroy();}@Overrideprotected void onResume() {super.onResume();findViewById(R.id.progressBar).setVisibility(View.VISIBLE);if (!ijkVideoView.isPlaying()) {ijkVideoView.start();}}@Overrideprotected void onPause() {super.onPause();ijkVideoView.pause();}
}

第二种:Vitamio

Vitamio是适用于Android和iOS的开放式多媒体框架,具有完整,真实的硬件加速解码器和渲染器。

新的功能:

  1. 支持大多数FFmpeg AVOptions,从而启用自定义HTTP标头支持。
  2. 支持更多的硬件,例如X86或MIPS。
  3. 改善流媒体,特别是支持自适应比特率流媒体,需要手动打开。
  4. 包含OpenSSL,因此支持某些与SSL相关的协议,例如https,tls,rtmps,rtmpts。
  5. 播放速度控制从0.5倍到2.0倍。
  6. 改进的字幕支持,包括外部位图字幕。
  7. 在线视频缓存到本地存储中,并且可以重复使用,直到删除缓存文件为止。
  8. 更多MediaPlayer API,例如getMetadata,getVideoTrack。
  9. 完整的Java代码对所有开发人员开放,欢迎修改和贡献。
    10.支持RGBA_8888渲染​​,支持将RGB_565或RGBA_8888切换到视频渲染。

Vitamio 感觉是强大的,播放速度也很快,也可以截图,但在使用过程也遇到了问题:
根据文档的提示,Vitamio 是可以 支持 https 的,但使用的结果是 https 无法播放,所以放弃了使用,也不知道是不是因为安全证书出了问题,还有一个问题就是,Vitamio 最早是更新于七年前,很多方法已经过时了,不知道还有没有人在维护。

Vitamio gitHub地址:https://github.com/yixia/VitamioBundleStudio

使用:
1)、解压文件,将其中的vitamio导入到as中

其中的vitamio-sample是官方提供的demo,而我们要导入as的是vitamio.

2)、打开AS,File -> New -> Import Moudle,选择刚才解压文件夹下的 vitamio 文件.
导入后的文件目录中会多出vitamin文件夹,如下图

3)、在 dependencies 中添加 compile project(’:vitamio’) 如果你导入module中更改过名字的话 要改成修改后的名字 如图:

混淆配置:

# For Vitamio classes
-keep class io.vov.utils.** { *; }
-keep class io.vov.vitamio.** { *; }

4)、打开app/src/main目录下的AndroidManifest.xml,注册io.vov.vitamio.activity.InitActivity

<activityandroid:name="io.vov.vitamio.activity.InitActivity"android:configChanges="orientation|screenSize|smallestScreenSize|keyboard|keyboardHidden|navigation"android:launchMode="singleTop"android:theme="@android:style/Theme.NoTitleBar"android:windowSoftInputMode="stateAlwaysHidden" />

注意:这个InitActivity存在于vitamio/src/对应的目录下,不需要用户编写.

添加权限:

<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

至此,vitamio导入完毕.

5)、VideoView控件的使用

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"><io.vov.vitamio.widget.CenterLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"android:background="@color/color_000000"android:orientation="vertical"><io.vov.vitamio.widget.VideoViewandroid:id="@+id/buffer"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center" /></io.vov.vitamio.widget.CenterLayout><ImageViewandroid:id="@+id/back"android:layout_width="wrap_content"android:layout_height="48dp"android:contentDescription="@null"android:paddingStart="13dp"android:paddingEnd="13dp"android:src="@mipmap/back_video" /><ProgressBarandroid:id="@+id/progressBar"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_centerInParent="true" /></RelativeLayout>
public class VideoActivity extends Activity implements MediaPlayer.OnInfoListener {private VideoView mVideoView;private AlertDialog alertDialog;private int errorCount;private int requestCode;private String shotsName;private String path;private MediaPlayer mediaPlayer;@Overridepublic void onCreate(Bundle icicle) {super.onCreate(icicle);Vitamio.isInitialized(this);requestWindowFeature(Window.FEATURE_NO_TITLE);getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);setContentView(R.layout.vitamio_layout);mVideoView = findViewById(R.id.buffer);findViewById(R.id.back).setOnClickListener(v -> onBackPressed());AlertDialog.Builder builder = new AlertDialog.Builder(this);builder.setTitle("播放错误");builder.setNegativeButton("重试", (dialogInterface, i) -> {setLiveParam();mVideoView.resume();});builder.setPositiveButton("退出", (dialogInterface, i) -> finish());alertDialog = builder.create();alertDialog.setCancelable(false);alertDialog.setCanceledOnTouchOutside(false);setLiveParam();}private void setLiveParam() {errorCount = 0;findViewById(R.id.progressBar).setVisibility(View.VISIBLE);Intent intent = getIntent();path = intent.getStringExtra("ADDRESS");requestCode = intent.getIntExtra("requestCode", 200);shotsName = intent.getStringExtra("shotsName");Uri uri = Uri.parse(path);mVideoView.setVideoURI(uri);mVideoView.setBufferSize(1024);mVideoView.setHardwareDecoder(true);mVideoView.requestFocus();mVideoView.setOnInfoListener(this);mVideoView.setOnPreparedListener(mediaPlayer -> {// optional need Vitamio 4.0mediaPlayer.setPlaybackSpeed(1.0f);});mVideoView.setOnErrorListener((mediaPlayer, i, i1) -> {errorCount++;if (errorCount > 3) {String errMsg = "MEDIA_ERROR_UNKNOWN";switch (i) {case MediaPlayer.MEDIA_ERROR_UNKNOWN:errMsg = "MEDIA_ERROR_UNKNOWN";break;case MediaPlayer.MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK:errMsg = "MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK";break;case MediaPlayer.MEDIA_ERROR_IO:errMsg = "MEDIA_ERROR_IO";break;case MediaPlayer.MEDIA_ERROR_MALFORMED:errMsg = "MEDIA_ERROR_MALFORMED";break;case MediaPlayer.MEDIA_ERROR_UNSUPPORTED:errMsg = "MEDIA_ERROR_UNSUPPORTED";break;case MediaPlayer.MEDIA_ERROR_TIMED_OUT:errMsg = "MEDIA_ERROR_TIMED_OUT";break;}alertDialog.setTitle("播放错误:" + errMsg);alertDialog.show();} else {new Handler().postDelayed(this::setLiveParam, 1000);}return true;});}@Overrideprotected void onDestroy() {if (alertDialog != null && alertDialog.isShowing())alertDialog.dismiss();super.onDestroy();}@Overrideprotected void onResume() {super.onResume();findViewById(R.id.progressBar).setVisibility(View.VISIBLE);}@Overridepublic boolean onInfo(MediaPlayer mp, int what, int extra) {mediaPlayer = mp;switch (what) {case MediaPlayer.MEDIA_INFO_BUFFERING_START:if (mVideoView.isPlaying()) {mVideoView.pause();}findViewById(R.id.progressBar).setVisibility(View.VISIBLE);break;case MediaPlayer.MEDIA_INFO_BUFFERING_END:mVideoView.start();findViewById(R.id.progressBar).setVisibility(View.INVISIBLE);break;case MediaPlayer.MEDIA_INFO_DOWNLOAD_RATE_CHANGED:LogUtils.Log(getClass().getSimpleName(), "vitamio download rate" + extra + "kb/s");break;}return true;}// 捕获返回键的方法2@Overridepublic void onBackPressed() {if (requestCode == 200) {super.onBackPressed();} else {viewShot();}}/*** view截图** @return*/public void viewShot() {if (mediaPlayer != null) {// 核心代码startBitmap bitmap = mediaPlayer.getCurrentFrame();String savePath;if (bitmap != null) {String path = getExternalFilesDir(null).toString() + "/monitoringScreenshots/";File file = new File(path);if (!file.exists()) {file.mkdirs();}try {path = path + shotsName + ".png";File file2 = new File(path);if (file2.exists()) {file2.delete();}savePath = BitmapUtils.saveBitmapToFileWithFilePathWithOriginParams(path, bitmap).toString();setResult(savePath);} catch (IOException e) {e.printStackTrace();setResult("");}} else {setResult("");}} else {setResult("");}}/*** savePath 截图保存地址** @param savePath*/private void setResult(String savePath) {Intent intent = new Intent();intent.putExtra("screenshotPath", savePath);setResult(RESULT_OK, intent);finish();}
}

VideoView常用函数

/*** 获取扫描视频的Uri。* 参数layout(缩放参数)参见MediaPlayer的常量:VIDEO_LAYOUT_ORIGIN(原始大小)、VIDEO_LAYOUT_SCALE(画面全屏)、VIDEO_LAYOUT_STRETCH(画面拉伸)、VIDEO_LAYOUT_ZOOM(画面裁剪)、VIDEO_LAYOUT_FIT_PARENT(画面铺满)* 参数aspectRation(宽高比),为0将自动检测*/
public void setVideoLayout(int layout,float aspectRatio);
//Surface是否有效。 参见Surface的isValid方法。
public boolean isValid();
//设置视频路径。
public void setVideoPath(String path);
//设置视频URI。(可以是网络视频地址)
public void setVideoURI(Uri uri);
//停止视频播放,并释放资源。
public void stopPlayback();
/*** 设置媒体控制器。* 参数controller:媒体控制器,注意是io.vov.vitamio.widget.MediaController。*/
public void setMediaController(MediaController controller);
//注册一个回调函数,在视频预处理完成后调用。在视频预处理完成后被调用。此时视频的宽度、高度、宽高比信息已经获取到,此时可调用seekTo让视频从指定位置开始播放。
public void setOnPreparedListener(OnPreparedListener l);
//获取当前播放位置。
public long getCurrentPosition();
//设置播放位置。单位毫秒
public void seekTo(long msec);
//是否正在播放。
public boolean isPlaying();
//获取缓冲百分比。
public int getBufferPercentage();
/*** 设置视频质量。* 参数quality参见MediaPlayer的常量:VIDEOQUALITY_LOW(流畅)、VIDEOQUALITY_MEDIUM(普通)、VIDEOQUALITY_HIGH(高质)*/
public void setVideoQuality(int quality);
//设置视频缓冲大小。默认1024KB,单位byte
public void setBufferSize(int bufSize);
//检测是否缓冲完毕。
public boolean isBuffering();
//设置元数据编码。例如:UTF-8
public void setMetaEncoding(String encoding);

第三种:ExoPlayer

ExoPlayer是构建在Android低水平媒体API之上的一个应用层媒体播放器。和Android内置的媒体播放器相比,ExoPlayer有许多优点。ExoPlayer支持内置的媒体播放器支持的所有格式外加自适应格式DASH和SmoothStreaming。ExoPlayer可以被高度定制和扩展以适应不同的使用场景。

优点:

  1. 支持HTTP上的动态自适应流DASH和SmoothStreaming。更多详情请参看 Supported formats。
  2. 支持高级的HLS特点,例如正确的处理#EXT-X-DISCONTINUITY标签。
  3. 能够无缝的合并,串联,循环播放媒体文件。
  4. 能够被高度扩展和定制,以适用不同的场景。
  5. 加载速度快
  6. 支持视频截图
  7. 支持自定义组件

缺点:

  1. 在某些设备上播放音频,ExoPlayer可能会比MediaPlayer消耗更多的电量。

ExoPlayer gitHub 地址:https://github.com/google/ExoPlayer/

集成方式:

implementation 'com.google.android.exoplayer:exoplayer:2.13.2'

使用:

class ExoplayerActivity : Activity(), Player.EventListener {var path: String? = nullprivate var simpleExoPlayer: SimpleExoPlayer? = nullprivate var requestCode = 0override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)requestWindowFeature(Window.FEATURE_NO_TITLE)window.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN)setContentView(R.layout.activity_exoplayer)path = intent.getStringExtra("ADDRESS")requestCode = intent.getIntExtra("requestCode", 200)initView()}private fun initView() {simpleExoPlayer = SimpleExoPlayer.Builder(this).build()path?.let {val mediaItem: MediaItem = MediaItem.fromUri(it)simpleExoPlayer?.setMediaItem(mediaItem)simpleExoPlayer?.prepare()}simpleExoPlayer?.addListener(this)exoplayer_videoView.player = simpleExoPlayerfindViewById<View>(R.id.exoplayer_back).setOnClickListener { onBackPressed() }}override fun onPlaybackStateChanged(state: Int) {//        Player.STATE_IDLE -> "ExoPlayer.闲置状态      -"
//        Player.STATE_BUFFERING -> "ExoPlayer.国家缓冲 -"
//        Player.STATE_READY -> "ExoPlayer.准备就绪     -"
//        Player.STATE_ENDED -> "ExoPlayer.状态已结束     -"
//        else -> "UNKNOWN_STATEwhen (state) {Player.STATE_READY -> {exoplayer_progressBar.visibility = View.GONE}else -> {}}}override fun onResume() {super.onResume()simpleExoPlayer?.play()}override fun onStop() {super.onStop()simpleExoPlayer?.pause()}override fun onDestroy() {super.onDestroy()simpleExoPlayer?.removeListener(this)simpleExoPlayer?.release()}// 捕获返回键的方法2@Overrideoverride fun onBackPressed() {if (requestCode == 200) {super.onBackPressed()} else {saveBitmap()}}/*** 获取并保存截图*/private fun saveBitmap() {try {val textureView: TextureView = exoplayer_videoView.videoSurfaceView as TextureViewval screenshotBitmap = textureView.bitmapval path = getExternalFilesDir(null).toString() + "/monitoringScreenshots/"val file = File(path)if (!file.exists()) {file.mkdirs()}//文件名为时间val timeStamp = System.currentTimeMillis()val sdf = SimpleDateFormat("yyyyMMdd_HHmmss")val sd = sdf.format(Date(timeStamp))val fileName = "$sd.jpg"val savePath = path + fileNameval outputStream = FileOutputStream(savePath)screenshotBitmap.compress(Bitmap.CompressFormat.JPEG, 100, outputStream)outputStream.flush()outputStream.close()setResult(savePath)} catch (e: IOException) {e.printStackTrace()finish()}}/*** savePath 截图保存地址** @param savePath*/private fun setResult(savePath: String) {val intent = Intent()intent.putExtra("screenshotPath", savePath)setResult(RESULT_OK, intent)finish()}
}
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="match_parent"android:background="@color/color_000000"tools:context="com.tzyun.pip.ijkPlayer.ExoplayerActivity"><com.google.android.exoplayer2.ui.PlayerViewandroid:id="@+id/exoplayer_videoView"android:layout_width="0dp"android:layout_height="0dp"app:auto_show="false"app:use_controller="false"app:surface_type="texture_view"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintLeft_toLeftOf="parent"app:layout_constraintRight_toRightOf="parent"app:layout_constraintTop_toTopOf="parent" /><ImageViewandroid:id="@+id/exoplayer_back"android:layout_width="wrap_content"android:layout_height="48dp"android:contentDescription="@null"android:paddingStart="13dp"android:paddingEnd="13dp"android:src="@mipmap/back_video"app:layout_constraintLeft_toLeftOf="parent"app:layout_constraintTop_toTopOf="parent" /><ProgressBarandroid:id="@+id/exoplayer_progressBar"android:layout_width="wrap_content"android:layout_height="wrap_content"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintLeft_toLeftOf="parent"app:layout_constraintRight_toRightOf="parent"app:layout_constraintTop_toTopOf="parent" /></androidx.constraintlayout.widget.ConstraintLayout>

Android m3u8网络视频播放相关推荐

  1. Android的网络视频播放

    需要先在AndroidManifest中加入网络权限 <uses-permission android:name="android.permission.INTERNET" ...

  2. 简易网络视频播放器android

    简易网络视频播放器android demo: Qvod 1.新建: app\src\main\res\xml\network_security_config.xml 作用是可以发送 http请求 &l ...

  3. Android实战练习——简单的网络视频播放器

    项目目录 一.项目概述 二.开发环境 三.项目结构 四.运行演示 五.项目总结 六.源码获取 一.项目概述 本次项目实现了一个简单的网络视频播放器,调用的是高度自定义的视频框架jiaozivideop ...

  4. 22_Android中的本地音乐播放器和网络音乐播放器的编写,本地视频播放器和网络视频播放器,照相机案例,偷拍案例实现

    1 编写以下案例: 当点击了"播放"之后,在手机上的/mnt/sdcard2/natural.mp3就会播放. 2 编写布局文件activity_main.xml <Line ...

  5. Android MediaPalyer实现视频播放

    Android中常见音.视频支持的格式 视频 音频 3GPP.MPEG-4 MP3.3GPP.Ogg.WAVE MediaPalyer 实例化分两种情况: 使用new 关键字: setDataSour ...

  6. Android播放网络视频截图

    Android播放网络视频截图 最近博主遇到一个Android电视的开发项目,项目需要电视客户端播放服务器端视频,通过遥控器一键截图,并将截图云推送到手机客户端,于是博主就开始找度神去求助了,毕竟以前 ...

  7. Android开发 VideoView视频播放详解

    前言 VideoView是Android主要的视频播放View,它其实是对MediaPlayer的再次封装.如果你已经了解过MediaPlayer在使用VideoView是十分简单的.如果你想先了解M ...

  8. Android中网络使用

    Android中判断网络连接是否可用 一.判断网络连接是否可用 public static boolean isNetworkAvailable(Context context) { Connecti ...

  9. android 代码获取图片信息吗,Android 通过网络获取图片的代码

    Android 通过网络获取图片的代码 主activity package com.netimg; import android.app.Activity; import android.graphi ...

  10. android 检查网络连接状态实现步骤

     android 如何检查网络连接状态,是android开发中一个常见的问题,本文将介绍如何实现,需要的朋友可以参考下 获取网络信息需要在AndroidManifest.xml文件中加入相应的权限 ...

最新文章

  1. 生命起源之谜:RNA世界假说将迎来终结?
  2. firewalld的9个zone、firewalld关于zone和service的操作
  3. 正则表达式调用“或”变量
  4. @清晰掉 GNU C __attribute__
  5. Bug整理——Spring boot 执行Junit Test时不加载Application中的参数的问题
  6. Java中的复合设计模式
  7. c# winform 点击按钮切换tabcontrol标签
  8. matplotlib绘制平滑曲线
  9. Faster R-CNN源码中ROI Pooling的解析
  10. 年度回顾 | 2019 年的 Apache Flink(文末有福利)
  11. Zemax操作--2(单透镜和双胶合透镜优化)
  12. umd文件结构深度解剖
  13. 互联网上的UFO教派
  14. SiriKit测试全攻略
  15. eclipse中格式自动调整的问题
  16. mysql load data 更新_mysql 用load data 导入数据时,数据被截断问题 | 学步园
  17. grpc-go源码剖析二十之grpc客户端帧接收器是如何处理不同的帧的?
  18. 软件开发随笔系列一——分布式架构实现
  19. git clone报错:repository ‘xxxxxxxxx’does not exist
  20. resnet-50介绍(一)

热门文章

  1. PotPlayer+SVP4视频补帧简易教程
  2. C#通过NOPI读写Excel,并插入图片,VS2019
  3. C语言面试题小练——第8天:static的作用、全局变量与局部变量的存储空间、宏定义注意点
  4. 夜神模拟器连接手柄无反应_夜神模拟器怎么连接手柄?夜神模拟器连接手柄具体操作...
  5. 蓝牙耳机的两种通讯协议及奥秘
  6. 记一次maven打包命令及指定pom文件
  7. brctl 设置ip_Linux 网桥配置命令:brctl
  8. 易康eCognition9.0安装教程-附软件安装包
  9. 【视频直播篇七】Aliplayer的使用
  10. linux 查看网卡厂商,linux下查看网卡信息的命令