谷歌的一个在线播放视频的框架,使用起来非常简单

1.官方地址:https://github.com/google/ExoPlayer 有兴趣的,可以去官方地址,看一下框架的源码,本例子只是介绍简单使用

2.我的项目地址:https://github.com/wrs13634194612/ExoPlayerTest

3.运行效果图:

4.首先就是引用,在builder.gradle引入框架,或者直接使用jar包,本例子是使用的jar包

    implementation 'com.google.android.exoplayer:exoplayer-core:2.7.3'
    implementation 'com.google.android.exoplayer:exoplayer-ui:2.7.3'
    implementation 'com.google.android.exoplayer:exoplayer-core:2.7.3'

引入jar:

implementation files('libs/exoplayer.jar')

5.清单文件,设置一下硬件加速解码和网络请求权限,并且设置activity横屏

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="com.example.administrator.testz"
    tools:ignore="MissingLeanbackLauncher">


    <uses-feature
        android:name="android.software.leanback"
        android:required="false" />
    <uses-feature
        android:name="android.hardware.touchscreen"
        android:required="false" />

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

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme"
        tools:ignore="GoogleAppIndexingWarning">
        <activity android:name=".MainActivity"
            android:screenOrientation="landscape">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

    </application>

</manifest>

6.布局:简单写一个播放器控件,展示视频播放内容

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/root"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:keepScreenOn="true">

    <com.google.android.exoplayer.VideoSurfaceView
        android:id="@+id/surface_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_gravity="center" />

    <View
        android:id="@+id/shutter"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@android:color/black" />

</FrameLayout>

7.创建一个视频类,

package com.example.administrator.testz;

public class Video {
    public static final String INTENT_EXTRA_VIDEO = "INTENT_EXTRA_VIDEO";
    private long id;
    private int year;
    private int rating;
    private String description;
    private String title;
    private String thumbUrl;
    private String contentUrl;
    private String category;
    private String tags;


    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public int getYear() {
        return year;
    }

    public void setYear(int year) {
        this.year = year;
    }

    public int getRating() {
        return rating;
    }

    public void setRating(int rating) {
        this.rating = rating;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getThumbUrl() {
        return thumbUrl;
    }

    public void setThumbUrl(String thumbUrl) {
        this.thumbUrl = thumbUrl;
    }

    public String getContentUrl() {
        return contentUrl;
    }

    public void setContentUrl(String contentUrl) {
        this.contentUrl = contentUrl;
    }

    public String getCategory() {
        return category;
    }

    public void setCategory(String category) {
        this.category = category;
    }

    public String getTags() {
        return tags;
    }

    public void setTags(String tags) {
        this.tags = tags;
    }


}

8.创建ExoPlayer对象,获取视频数据,展示,具体的网络请求,视频的编解码,都已经封装好了,记得退出activity需要清空ExoPlayer对象,避免内存泄漏

package com.example.administrator.testz;

import androidx.appcompat.app.AppCompatActivity;

import android.media.MediaCodec;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.View;
import android.widget.MediaController;
import android.widget.Toast;

import com.google.android.exoplayer.ExoPlaybackException;
import com.google.android.exoplayer.ExoPlayer;
import com.google.android.exoplayer.FrameworkSampleSource;
import com.google.android.exoplayer.MediaCodecAudioTrackRenderer;
import com.google.android.exoplayer.MediaCodecTrackRenderer;
import com.google.android.exoplayer.MediaCodecVideoTrackRenderer;
import com.google.android.exoplayer.SampleSource;
import com.google.android.exoplayer.TrackRenderer;
import com.google.android.exoplayer.VideoSurfaceView;
import com.google.android.exoplayer.util.PlayerControl;

public class MainActivity extends AppCompatActivity implements SurfaceHolder.Callback,
        ExoPlayer.Listener, MediaCodecVideoTrackRenderer.EventListener {

    private static final int RENDERER_COUNT = 2;
    private static final String TAG = "MainActivity";
    private Video mViedeo;
    private MediaController mediaController;
    private View shutterView;
    private VideoSurfaceView surfaceView;
    private ExoPlayer player;
    private MediaCodecVideoTrackRenderer videoRenderer;
    private boolean autoPlay;
    private String mUrl = "https://v-cdn.zjol.com.cn/280443.mp4";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_player);

        mViedeo = (Video)getIntent().getSerializableExtra(Video.INTENT_EXTRA_VIDEO);
        View root = findViewById(R.id.root);
        shutterView = findViewById(R.id.shutter);
        surfaceView = (VideoSurfaceView)findViewById(R.id.surface_view);

        mediaController = new MediaController(this);
        mediaController.setPadding(48,27,48,27);
        mediaController.setAnchorView(root);
        surfaceView.getHolder().addCallback(this);
        preparePlayer();
    }

    private void preparePlayer() {
        SampleSource sampleSource = new FrameworkSampleSource(this, Uri.parse(mUrl),null,RENDERER_COUNT);
        videoRenderer = new MediaCodecVideoTrackRenderer(sampleSource,MediaCodec.VIDEO_SCALING_MODE_SCALE_TO_FIT);
        TrackRenderer audioRenderer = new MediaCodecAudioTrackRenderer(sampleSource);

        player = ExoPlayer.Factory.newInstance(RENDERER_COUNT,1000,5000);
        player.addListener(this);
        mediaController.setMediaPlayer(new PlayerControl(player));
        mediaController.setEnabled(true);
        player.prepare(videoRenderer,audioRenderer);
    }

    @Override
    protected void onResume() {
        super.onResume();
    }

    @Override
    protected void onPause() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            requestVisibleBehind(true);
        }
        super.onPause();
    }

    // 当此activity的半透明activity变得不透明或正在启动另一个activity时调用。
    @Override
    public void onVisibleBehindCanceled() {
        super.onVisibleBehindCanceled();
        if (player!=null){
            player.release();
            player = null;
        }
        videoRenderer = null;
        shutterView.setVisibility(View.VISIBLE);
    }

    @Override
    protected void onStop() {
        super.onStop();
        if (player!=null){
            player.release();
            player = null;
        }
        videoRenderer = null;
        shutterView.setVisibility((View.VISIBLE));
    }

    private void maybeStartPlayback(){
        Log.d(TAG, "maybeStartPlayback");
        Surface surface = surfaceView.getHolder().getSurface();
        if (videoRenderer == null || surface == null || !surface.isValid()){
            return;
        }
        player.sendMessage(videoRenderer,MediaCodecVideoTrackRenderer.MSG_SET_SURFACE,surface);
        if (autoPlay){
            player.setPlayWhenReady(true);
            autoPlay = false;
        }
    }

    private void onError(Exception e){
        Log.e(TAG,"Player failed",e);
        Toast.makeText(getApplicationContext(),"Playback failed",Toast.LENGTH_SHORT).show();
        finish();
    }

    @Override
    public void surfaceCreated(SurfaceHolder surfaceHolder) {
        Log.e(TAG,"maybeStartPlayback");
        maybeStartPlayback();
    }

    @Override
    public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i1, int i2) {

    }

    @Override
    public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
        if (videoRenderer != null){
            player.blockingSendMessage(videoRenderer,MediaCodecVideoTrackRenderer.MSG_SET_SURFACE,null);
        }
    }

    @Override
    public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
        Log.e(TAG,"player state"+playbackState);
        if (playbackState == ExoPlayer.STATE_READY){
            shutterView.setVisibility(View.GONE);
            mediaController.show(0);
        }
    }



    @Override
    public void onPlayWhenReadyCommitted() {

    }

    @Override
    public void onPlayerError(ExoPlaybackException e) {
        onError(e);
    }

    @Override
    public void onDroppedFrames(int count, long elapsed) {
        Log.e(TAG,"Dropped frames:"+count);
    }

    @Override
    public void onVideoSizeChanged(int width, int height, float v) {
        surfaceView.setVideoWidthHeightRatio(height==0?1:(float)width/height);
    }

    @Override
    public void onDrawnToSurface(Surface surface) {
        shutterView.setVisibility(View.GONE);
        mediaController.show(0);
    }

    @Override
    public void onDecoderInitializationError(MediaCodecTrackRenderer.DecoderInitializationException e) {

    }

    @Override
    public void onCryptoError(MediaCodec.CryptoException e) {

    }

    @Override
    public void onDecoderInitialized(String s, long l, long l1) {

    }
}

end