当前位置:   article > 正文

Android ExoPlayer 集成使用以及源码分析

Android ExoPlayer 集成使用以及源码分析

一、简要介绍ExoPlayer

  • Android框架本身提供了MediaPlayer来实现媒体播放。还提供了其他媒体api框架,如MediaCodec、AudioTrack和MediaDrm,可用于构建自定义媒体播放器解决方案。
  • ExoPlayer是一款开源的应用级媒体播放器,可以参考官网Git
  • ExoPlayer是Android的应用程序级媒体播放器。 它提供了Android的MediaPlayer API的替代品,用于在本地和互联网上播放音频和视频。 ExoPlayer支持Android MediaPlayer API目前不支持的功能,包括DASH和SmoothStreaming自适应回放。 与MediaPlayer API不同,ExoPlayer易于定制和扩展,并可通过Play Store应用程序更新进行更新。
    在这里插入图片描述

二、项目开发集成和使用ExoPlayer

1 、入门使用:

  • 将ExoPlayer添加为项目的依赖项。
    确保build.gradle的项目根目录中的文件中包含Google和JCenter存储库
repositories {
    google()
    jcenter()
}
  • 1
  • 2
  • 3
  • 4
添加ExoPlayer模块:

在build.gradle您的应用模块的文件中添加一个依赖项。以下内容将对完整的ExoPlayer库添加依赖项:
这里我们选择最新的:
在这里插入图片描述

implementation 'com.google.android.exoplayer:exoplayer:2.11.7'
  • 1

作为完整库的替代方法,可以仅依赖实际需要的库模块。例如,以下内容将添加对Core,DASH和UI

库模块的依赖关系,这可能是播放DASH内容的应用程序所必需的:

implementation 'com.google.android.exoplayer:exoplayer-core:2.11.7'
implementation 'com.google.android.exoplayer:exoplayer-dash:2.11.7'
implementation 'com.google.android.exoplayer:exoplayer-ui:2.2.11.7'
  • 1
  • 2
  • 3

在这里插入图片描述

可用的库模块在下面列出。向完整的ExoPlayer库添加依赖项等效于分别为所有库模块添加依赖项。

库名称主要功能
exoplayer-cor核心功能
exoplayer-dash支持DASH内容
exoplayer-hlsr支持HLS内容
exoplayer-smoothstreamingr支持SmoothStreaming内容
exoplayer-uir与ExoPlayer一起使用的UI组件和资源

除了库模块之外,ExoPlayer还具有多个扩展模块,这些扩展模块依赖于外部库来提供附加功能。浏览 扩展目录及其各自的自述文件以了解详细信息。
在这里插入图片描述

  • 创建一个MyExoPlayerDemo工程。
    在这里插入图片描述
需要开启JAVA8
compileOptions {
  targetCompatibility JavaVersion.VERSION_1_8
}
  • 1
  • 2
  • 3

在这里插入图片描述

在这里插入图片描述

有兴趣的可以自己复写源码,增加定制功能;

创建播放器

可以使用SimpleExoPlayer.Builder或 创建实例ExoPlayer.Builder。这些构建器提供了一系列用于创建ExoPlayer实例的定制选项。对于绝大多数用例, SimpleExoPlayer.Builder都应使用。此构建器返回 SimpleExoPlayer,它扩展ExoPlayer为添加其他高级播放器功能。以下代码是创建的示例SimpleExoPlayer。

SimpleExoPlayer mPlayer = new SimpleExoPlayer.Builder(mContext).build();
  • 1

在这里插入图片描述

  • 将播放器连接到视图(用于视频输出和用户输入)。
  • 准备播放器MediaSource以播放。
  • 完成后释放播放器。

可以参考个人Exoplayer Demo Git:

https://github.com/xiaxiaxa/ExoPlayerDemo.git

package com.mgtv.exodemo.activity;

import android.content.Context;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.net.Uri;
import android.util.Log;

import com.google.android.exoplayer2.SimpleExoPlayer;
import com.google.android.exoplayer2.source.MediaSource;
import com.google.android.exoplayer2.source.hls.HlsMediaSource;
import com.google.android.exoplayer2.ui.PlayerView;
import com.google.android.exoplayer2.upstream.DataSource;
import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter;

import com.mgtv.exodemo.MyApplication;
import com.mgtv.exodemo.R;


public class ExoPlayerActivity extends BaseActivity {

    private Context mContext;
    private PlayerView mPlayerView = null;
    private SimpleExoPlayer mPlayer;
    private DataSource.Factory mediaDataSourceFactory;
    private MediaSource mediaSourceHls;
    //    private UdpDataSource udpDataSourceRtp;
    private static final DefaultBandwidthMeter BANDWIDTH_METER = new DefaultBandwidthMeter();
    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";


    private void initExoPlayer() {
        mPlayer = new SimpleExoPlayer.Builder(mContext).build();
        mPlayerView.setPlayer(mPlayer);
        Uri playHlsUri = Uri.parse(videoUrlHls);
//        Uri playRtpUri = Uri.parse(videoUrlRtp);
        /**使用dash的解析库*/
/*        dashMediaSource = new DashMediaSource(playHlsUri,mediaDataSourceFactory,
                new DefaultDashChunkSource.Factory(mediaDataSourceFactory
        ),null,null);*/

        /**====解析hls===begin*/
        mediaSourceHls = new HlsMediaSource.Factory(mediaDataSourceFactory)
                .createMediaSource(playHlsUri, null, null);
        Log.d("exo", "Mr.xw==mediaSource==" + mediaSourceHls);
        mPlayer.prepare(mediaSourceHls);
        mPlayerView.setPlayer(mPlayer);
        mPlayer.setPlayWhenReady(true);
        /**====解析hls===end*/

        /**====解析rtp===begin 这块需要复写udp方法,暂未实现*/

/*        mediaSourceHls = new UdpDataSource(mediaDataSourceFactory)
                .createMediaSource(playHlsUri, null, null);
        Log.d("exo", "Mr.xw==mediaSource==" + mediaSourceHls);
        mPlayerView.setPlayer(mPlayer);
        mPlayer.setPlayWhenReady(true);*/
        /**====解析rtp===end*/
        

    }


    @Override
    public int getLayoutRes() {
        getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
        return R.layout.exoplayer;
    }

    @Override
    public void init() {
        mContext = this;
        mPlayerView = findViewById(R.id.exo_vr);
        mediaDataSourceFactory = buildDataSourceFactory(true);
        initExoPlayer();
    }

    private DataSource.Factory buildDataSourceFactory(boolean useBandwidthMeter) {
        return ((MyApplication) getApplication())
                .buildDataSourceFactory(useBandwidthMeter ? null : null);
    }

    @Override
    public void onPointerCaptureChanged(boolean hasCapture) {

    }

    @Override
    protected void onStop() {
        super.onStop();

    }


    @Override
    protected void onPause() {
        super.onPause();
        releaseData();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        releaseData();
    }

    private void releaseData() {
        if (isFinishing()) {
            if (mPlayer != null) {
                mPlayer.stop();
                mPlayer.release();
                mPlayer = null;
            }
            if (mPlayerView!=null){
                mPlayerView.destroyDrawingCache();
                mPlayerView = null;
            }
        }
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
apply plugin: 'com.android.application'

android {
    compileSdkVersion 29
    buildToolsVersion "30.0.0"
    defaultConfig {
        applicationId "com.mgtv.exodemo"
        minSdkVersion 19
        targetSdkVersion 29
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }
    compileOptions {
        targetCompatibility JavaVersion.VERSION_1_8
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    implementation 'com.google.android.exoplayer:exoplayer:2.11.7'
    implementation 'com.google.android.exoplayer:exoplayer-core:2.11.7'
    implementation 'com.google.android.exoplayer:exoplayer-dash:2.11.7'
    implementation 'com.google.android.exoplayer:exoplayer-hls:2.11.7'
    implementation 'com.google.android.exoplayer:exoplayer-ui:2.11.7'
    implementation 'com.google.android.exoplayer:extension-rtmp:2.11.7'
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation files('libs/xutils.jar')
    implementation 'androidx.appcompat:appcompat:1.1.0'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test.ext:junit:1.1.1'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38

ExoPlayer库的核心是ExoPlayer接口。Exoplayer把播放类型、存储位置和渲染方式等任务委派给不同的部件,然后在创建播放器或后台播放的时候把这些部件注入。这些部件包括:

  • MediaSource - 负责装载 media,装载到MediaSource 的 media 可以被读取,MediaSource 在调用
    ExoPlayer.prepare 方法时被注入。
  • Render S - 用于渲染 media 的部件,在创建播放器时被注入。
  • TrackSelector - 从MediaSource 中选出 media 提供给可用的 Render S
    来渲染,在创建播放器时被注入。
  • LoadControl - 控制 MediaSource 缓存更多的 media,有多少 media 被缓冲。在创建播放器时被注入。

如果标准实现不能满足需求,也可以使用自定义实现。例如,自定义LoadControl 可以改变播放器的缓冲策略,或自定义Renderer 可实现 Android 本身不支持的视频编解码器。

ExoPlayer提供默认的音频和视频渲染器,利用了Android框架中的MediaCodec和AudioTrack类。这两个都需要一个SampleSource对象中注入,用来实现媒体示例的播放。

组件的注入在当前ExoPlayer库中是普遍存在的。下图展示了使用一个ExoPlayer来配置和播放MP4媒体流的高级对象模型。默认的音频和视频渲染器已经被注解到ExoPlayer中。ExtractorSampleSource类的实现被注解到渲染器中用于提供简单的媒体播放功能。DataSource和Extractor示例被注解到ExtractorSampleSource来支持加载媒体流和在被加载的数据中提取样板。在这个示例中DefaultUriDataSource和Mp4Extractor被用于播放从URIs中导入的MP4流。

可以参考官网文档:

[developer guide]:

https://google.github.io/ExoPlayer/guide.html
  • 1

[class reference]:

https://google.github.io/ExoPlayer/doc/reference
  • 1

[release notes]:

 https://github.com/google/ExoPlayer/blob/release-v2/RELEASENOTES.md
  • 1

[developer blog]:

 https://medium.com/google-exoplayer
  • 1

2.MediaSource

我们重点看下MediaSource的使用;

在ExoPlayer中,每个media都由 MediaSource 表示。 ExoPlayer库为DASH(DashMediaSource),SmoothStreaming(SsMediaSource),HLS(HlsMediaSource)和常规媒体文件(ExtractorMediaSource)提供了MediaSource实现。 在 main demo app 的PlayerActivity中可以找到如何实例化所有四个示例。

需要注意:MediaSource实例不适用于重新使用的情况。 如果您想用相同的media多次准备播放器,请每次使用新的实例。

除了上述的MediaSource实现外,ExoPlayer库还提供了MergingMediaSource,LoopingMediaSource,ConcatenatingMediaSource和DynamicConcatenatingMediaSource。这些MediaSource实现可以通过组合来实现更复杂的播放功能。下面描述了一些常见的用例。请注意,尽管在视频播放的上下文中描述了以下示例,但它们同样适用于仅播放音频,以及任何支持的媒体类型的播放的情况。

序号主要功能
1从侧面加载字幕文件
2循环播放视频
3播放一系列视频
4高级组合
(1)从侧面加载字幕文件
// Build the video MediaSource.
MediaSource videoSource =
    new ExtractorMediaSource.Factory(...).createMediaSource(videoUri);
// Build the subtitle MediaSource.
Format subtitleFormat = Format.createTextSampleFormat(
    id, // An identifier for the track. May be null.
    MimeTypes.APPLICATION_SUBRIP, // The mime type. Must be set correctly.
    selectionFlags, // Selection flags for the track.
    language); // The subtitle language. May be null.
MediaSource subtitleSource =
    new SingleSampleMediaSource.Factory(...)
        .createMediaSource(subtitleUri, subtitleFormat, C.TIME_UNSET);
// Plays the video with the sideloaded subtitle.
MergingMediaSource mergedSource =
    new MergingMediaSource(videoSource, subtitleSource);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
(2)循环播放视频

无限循环,通常最好使用 ExoPlayer.setRepeatMode 而不是 LoopingMediaSource。

MediaSource source =
    new ExtractorMediaSource.Factory(...).createMediaSource(videoUri);
// Plays the video twice.
LoopingMediaSource loopingSource = new LoopingMediaSource(source, 2);
  • 1
  • 2
  • 3
  • 4
(3)播放一系列视频

ConcatenatingMediaSource 可以连续播放两个或多个单独的MediaSource。 比如按顺序播放了两个视频的例子。 数据源之间的转换是无缝的。对连接的源具有相同的格式这一点不做强制要求,您可以把两个不同格式的数据源连接起来。

MediaSource firstSource =
    new ExtractorMediaSource.Factory(...).createMediaSource(firstVideoUri);
MediaSource secondSource =
    new ExtractorMediaSource.Factory(...).createMediaSource(secondVideoUri);
// Plays the first video, then the second video.
ConcatenatingMediaSource concatenatedSource =
    new ConcatenatingMediaSource(firstSource, secondSource);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

DynamicConcatenatingMediaSource 类似于 ConcatenatingMediaSource,不同之处在于它允许在播放前和播放期间动态添加,删除和移动MediaSource。 DynamicConcatenatingMediaSource非常适合于播放列表的使用场景,即用户可以在播放期间修改播放列表。

MediaSource 实例不应该多次添加到 DynamicConcatenatingMediaSource中,或者在之前被删除的情况下重新添加。 推荐创建新的实例去操作。

(4)高级组合

有可能进一步将复合MediaSources组合起来,用于更多不常见的用法。 给定两个视频A和B,以下示例显示LoopingMediaSource和ConcatenatingMediaSource如何一起使用来播放序列(A,A,B)。

ediaSource firstSource =
    new ExtractorMediaSource.Factory(...).createMediaSource(firstVideoUri);
MediaSource secondSource = 
    new ExtractorMediaSource.Factory(...).createMediaSource(secondVideoUri);
// Plays the first video twice.
LoopingMediaSource firstSourceTwice = new LoopingMediaSource(firstSource, 2);
// Plays the first video twice, then the second video.
ConcatenatingMediaSource concatenatedSource =
    new ConcatenatingMediaSource(firstSourceTwice, secondSource);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

或者

MediaSource firstSource = new ExtractorMediaSource.Builder(firstVideoUri, ...).build();
MediaSource secondSource = new ExtractorMediaSource.Builder(secondVideoUri, ...).build();
// Plays the first video twice, then the second video.
ConcatenatingMediaSource concatenatedSource =
     new ConcatenatingMediaSource(firstSource, firstSource, secondSource);
  • 1
  • 2
  • 3
  • 4
  • 5

除非文档明确允许,否则避免在组合中多次使用相同的MediaSource实例很重要。 上面的示例中使用firstSource两次就是这种情况,因为用于ConcatenatingMediaSource的Javadoc明确指出允许重复条目。 然而,一般来说,由构造组成的对象的图形应该是树形结构(这个地方不好翻译,英文不好见谅)。 在组合中使用多个等效的MediaSource实例是允许的。

最后

个人觉得使用ExoPlayer的一个特别之处是ExoPlayer支持数字版权管理(DRM)保护回放;

ExoPlayer库提供了一个DrmSessionManager的默认实现,名为DefaultDrmSessionManager,它使用MediaDrm。会话管理器支持在设备上存在模块DRM组件的任何DRM方案。所有的Android设备都需要支持Widevine模块DRM(使用L3安全性,尽管许多设备也支持L1)。某些设备可能支持其他方案,例如PlayReady。所有的Android TV 设备都支持PlayReady。

在这里插入图片描述

声明:本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号