使用AndroidX Media3的ExoPlayer播放网络视频

使用AndroidX Media3的ExoPlayer播放网络视频

AndroidX Media3是Google最新推出的媒体播放库,ExoPlayer是其核心播放引擎。下面详细介绍如何使用Media3库中的ExoPlayer播放网络视频。

一、环境配置

1. 添加Gradle依赖

dependencies {
    // Media3 ExoPlayer核心库
    implementation 'androidx.media3:media3-exoplayer:1.1.1'
    // UI组件
    implementation 'androidx.media3:media3-ui:1.1.1'
    // HLS支持
    implementation 'androidx.media3:media3-exoplayer-hls:1.1.1'
    // Dash支持
    implementation 'androidx.media3:media3-exoplayer-dash:1.1.1'
}

2. 添加网络权限

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

二、基本播放器实现

1. 布局文件 - activity_player.xml

<?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"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    
    <androidx.media3.ui.PlayerView
        android:id="@+id/player_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:surface_type="texture_view"
        app:controller_layout_id="@layout/custom_controller"
        app:use_controller="true"
        app:show_buffering="always" />
    
</androidx.constraintlayout.widget.ConstraintLayout>

2. Java代码 - PlayerActivity.java

public class PlayerActivity extends AppCompatActivity {
    
    private PlayerView playerView;
    private ExoPlayer player;
    private String videoUrl = "https://your-domain.com/video.mp4";
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_player);
        
        playerView = findViewById(R.id.player_view);
    }
    
    @Override
    protected void onStart() {
        super.onStart();
        initializePlayer();
    }
    
    @Override
    protected void onStop() {
        super.onStop();
        releasePlayer();
    }
    
    @Override
    protected void onPause() {
        super.onPause();
        if (player != null) {
            player.pause();
        }
    }
    
    private void initializePlayer() {
        // 创建ExoPlayer实例
        player = new ExoPlayer.Builder(this)
            .setSeekForwardIncrementMs(10000)  // 前进10秒
            .setSeekBackIncrementMs(10000)      // 后退10秒
            .setHandleAudioBecomingNoisy(true)  // 音频失去焦点时暂停
            .build();
        
        // 绑定播放器到视图
        playerView.setPlayer(player);
        
        // 设置媒体源
        MediaItem mediaItem = new MediaItem.Builder()
            .setUri(videoUrl)
            .setMimeType(MimeTypes.APPLICATION_MP4) // 可省略,自动检测
            .build();
        
        player.setMediaItem(mediaItem);
        player.prepare();
        player.play(); // 开始播放
    }
    
    private void releasePlayer() {
        if (player != null) {
            player.release();
            player = null;
        }
    }
}

三、高级功能实现

1. 自定义控制器布局

创建res/layout/custom_controller.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.media3.ui.PlayerControlView 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:button_fastforward="@drawable/exo_icon_fastforward"
    app:button_rewind="@drawable/exo_icon_rewind"
    app:show_timeout="3000">
    
    <TextView
        android:id="@+id/custom_text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="播放中..."
        android:textColor="@color/white"
        android:padding="8dp"/>
    
</androidx.media3.ui.PlayerControlView>

2. 自适应码率支持

private void createAdaptivePlayer() {
    // 创建自适应渲染器工厂
    RenderersFactory renderersFactory = new DefaultRenderersFactory(this)
        .forceEnableMediaCodecAdaptiveWorkaround();
    
    // 创建带宽计估算器
    DefaultBandwidthMeter bandwidthMeter = new DefaultBandwidthMeter.Builder(this).build();
    
    // 创建自适应数据源工厂
    DataSource.Factory dataSourceFactory = new DefaultHttpDataSource.Factory()
        .setBandwidthMeter(bandwidthMeter);
    
    // 创建播放器
    player = new ExoPlayer.Builder(this, renderersFactory)
        .setBandwidthMeter(bandwidthMeter)
        .setMediaSourceFactory(
            new DefaultMediaSourceFactory(this)
                .setDataSourceFactory(dataSourceFactory)
        )
        .build();
}

3. 播放列表支持

private void playMultipleVideos() {
    List<MediaItem> mediaItems = new ArrayList<>();
    mediaItems.add(new MediaItem.Builder()
        .setUri("https://.../video1.mp4")
        .setMediaMetadata(new MediaMetadata.Builder()
            .setTitle("视频一")
            .build())
        .build());
    mediaItems.add(new MediaItem.Builder()
        .setUri("https://.../video2.mp4")
        .setMediaMetadata(new MediaMetadata.Builder()
            .setTitle("视频二")
            .build())
        .build());
    
    player.setMediaItems(mediaItems);
    player.prepare();
    player.play();
    
    // 设置自动播放下一个
    player.setRepeatMode(Player.REPEAT_MODE_ALL);
}

4. HLS直播流播放

private void playHlsLiveStream() {
    MediaItem mediaItem = new MediaItem.Builder()
        .setUri("https://.../live.m3u8")
        .setLiveConfiguration(new MediaItem.LiveConfiguration.Builder()
            .setTargetOffsetMs(30000) // 延迟30秒
            .build())
        .build();
    
    player.setMediaItem(mediaItem);
    player.prepare();
    player.play();
}

5. DRM保护内容播放

private void playDrmProtectedContent() {
    MediaItem mediaItem = new MediaItem.Builder()
        .setUri("https://.../drm_video.mp4")
        .setDrmConfiguration(new MediaItem.DrmConfiguration.Builder(C.WIDEVINE_UUID)
            .setLicenseUri("https://.../license_server")
            .setMultiSession(true)
            .build())
        .build();
    
    player.setMediaItem(mediaItem);
    player.prepare();
    player.play();
}

四、监听播放事件

1. 播放状态监听

player.addListener(new Player.Listener() {
    @Override
    public void onPlaybackStateChanged(int playbackState) {
        switch (playbackState) {
            case Player.STATE_IDLE:
                // 播放器空闲
                break;
            case Player.STATE_BUFFERING:
                // 缓冲中
                break;
            case Player.STATE_READY:
                // 准备完成,可播放
                break;
            case Player.STATE_ENDED:
                // 播放结束
                break;
        }
    }
    
    @Override
    public void onPlayerError(PlaybackException error) {
        // 处理播放错误
        if (error.errorCode == PlaybackException.ERROR_CODE_IO_NETWORK_CONNECTION_FAILED) {
            showError("网络连接失败");
        }
    }
    
    @Override
    public void onIsPlayingChanged(boolean isPlaying) {
        // 播放状态变化
    }
});

2. 缓冲事件监听

player.addListener(new Player.Listener() {
    @Override
    public void onPlaybackParametersChanged(PlaybackParameters playbackParameters) {
        // 播放速度变化
    }
    
    @Override
    public void onPositionDiscontinuity(PositionInfo oldPosition, PositionInfo newPosition, int reason) {
        // 播放位置不连续
    }
});

3. 播放器统计信息

AnalyticsListener analyticsListener = new AnalyticsListener() {
    @Override
    public void onBandwidthEstimate(
        EventTime eventTime,
        int totalLoadTimeMs,
        long totalBytesLoaded,
        long bitrateEstimate
    ) {
        Log.d("PlayerStats", "带宽估计: " + bitrateEstimate / 1000 + " kbps");
    }
    
    @Override
    public void onVideoSizeChanged(EventTime eventTime, VideoSize videoSize) {
        Log.d("PlayerStats", "视频尺寸: " + videoSize.width + "x" + videoSize.height);
    }
};
player.addAnalyticsListener(analyticsListener);

五、播放器配置优化

1. 低延迟配置

private ExoPlayer createLowLatencyPlayer() {
    DefaultTrackSelector trackSelector = new DefaultTrackSelector(this);
    trackSelector.setParameters(
        trackSelector.buildUponParameters()
            .setMaxVideoSizeSd()
            .setPreferredVideoMimeTypes(MimeTypes.VIDEO_H264)
            .setForceLowLatency(true)
    );
    
    LoadControl loadControl = new DefaultLoadControl.Builder()
        .setBufferDurationsMs(500, 2000, 500, 500) // 最小缓冲500ms
        .build();
    
    return new ExoPlayer.Builder(this)
        .setTrackSelector(trackSelector)
        .setLoadControl(loadControl)
        .build();
}

2. 背景音频播放配置

<service
    android:name="androidx.media3.session.MediaSessionService"
    android:exported="true">
    <intent-filter>
        <action android:name="androidx.media3.session.MediaSessionService"/>
    </intent-filter>
</service>
private void setupMediaSession() {
    MediaSession mediaSession = new MediaSession.Builder(
        this, player)
        .setCallback(new MediaSession.Callback() {})
        .build();
    
    player.addListener(new Player.Listener() {
        @Override
        public void onEvents(Player player, Player.Events events) {
            if (events.contains(Player.EVENT_PLAYBACK_STATE_CHANGED)) {
                if (player.getPlaybackState() == Player.STATE_READY) {
                    mediaSession.setAvailableCommands(
                        mediaSession.getAvailableCommands()
                            .buildUpon()
                            .add(COMMAND_PLAY_PAUSE)
                            .add(COMMAND_STOP)
                            .build());
                }
            }
        }
    });
}

六、高级播放控制

1. 播放速度控制

// 设置播放速度
player.setPlaybackParameters(new PlaybackParameters(1.5f)); // 1.5倍速

// 显示速度控制UI
playerView.setShowFastForwardButton(true);
playerView.setShowPreviousButton(false);
playerView.setShowNextButton(false);
playerView.setShowFastForwardButton(true);
playerView.setShowRewindButton(true);

2. 视频质量选择

TrackSelectionDialogBuilder builder = 
    new TrackSelectionDialogBuilder(
        this,
        "选择视频质量",
        player,
        C.TRACK_TYPE_VIDEO);
    
builder.setTrackNameProvider(format -> {
    if (format.height == Format.NO_VALUE) return "未知";
    return format.height + "p | " + (format.bitrate / 1000) + "kbps";
});
    
builder.build().show();

3. 自定义重试策略

private void setupRetryPolicy() {
    player.setRetryHandler((handler, error) -> {
        int errorCode = PlaybackException.getErrorCode(error);
        
        if (errorCode == PlaybackException.ERROR_CODE_IO_NETWORK_CONNECTION_FAILED) {
            // 网络问题,延迟重试
            return new RetryHandler.Result(true, 2000, "等待网络恢复");
        } else {
            // 其他错误,重试3次
            if (handler.getRetryCount() < 3) {
                return new RetryHandler.Result(true, 3000, "重试: " + (handler.getRetryCount() + 1));
            }
        }
        return new RetryHandler.Result(false, "无法播放");
    });
}

七、性能优化

1. 内存优化配置

private ExoPlayer createOptimizedPlayer() {
    return new ExoPlayer.Builder(this)
        .setUseLazyPreparation(false)  // 及时准备
        .setHandleAudioBecomingNoisy(true)
        .setPauseAtEndOfMediaItems(true) // 播放结束时暂停
        .setLoadControl(new DefaultLoadControl.Builder()
            .setAllocator(new DefaultAllocator(true, 1024*1024)) // 减少内存分配
            .setBufferDurationsMs(
                500, 5000,   // 最小缓冲
                500, 500)    // 最大缓冲
            .build())
        .build();
}

2. 后台播放优化

@Override
public void onResume() {
    super.onResume();
    if (player != null) {
        player.setPlayWhenReady(true);
    }
}

@Override
public void onStop() {
    super.onStop();
    if (!isChangingConfigurations()) {
        releasePlayer();
    }
}

八、常见问题解决

播放错误处理

错误代码含义解决方案
ERROR_CODE_IO_NETWORK_CONNECTION_FAILED网络连接失败检查网络,重试播放
ERROR_CODE_DECODING_FAILED解码失败切换渲染器,尝试硬件加速
ERROR_CODE_PARSING_CONTAINER_MALFORMED格式错误验证媒体格式
ERROR_CODE_DECODER_INIT_FAILED解码器初始化失败检查设备支持的格式
ERROR_CODE_TIMEOUT操作超时增加超时设置

缓存控制

// 自定义缓存实现
CacheDataSource.Factory cacheDataSourceFactory = new CacheDataSource.Factory()
    .setCache(downloadCache)
    .setUpstreamDataSourceFactory(httpDataSourceFactory);

// 创建带缓存的播放器
player = new ExoPlayer.Builder(this)
    .setMediaSourceFactory(
        new ProgressiveMediaSource.Factory(cacheDataSourceFactory)
    ).build();

九、完整示例代码

高级播放器实现

public class EnhancedPlayerActivity extends AppCompatActivity {
    
    private PlayerView playerView;
    private ExoPlayer player;
    private Button qualityButton;
    private Button speedButton;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_enhanced_player);
        
        playerView = findViewById(R.id.player_view);
        qualityButton = findViewById(R.id.quality_button);
        speedButton = findViewById(R.id.speed_button);
        
        setupPlayer();
        setupListeners();
    }
    
    private void setupPlayer() {
        // 创建带宽计
        DefaultBandwidthMeter bandwidthMeter = new DefaultBandwidthMeter.Builder(this).build();
        
        // 创建数据源工厂
        DataSource.Factory dataSourceFactory = new DefaultHttpDataSource.Factory()
            .setBandwidthMeter(bandwidthMeter);
        
        // 创建自适应渲染器工厂
        RenderersFactory renderersFactory = new DefaultRenderersFactory(this)
            .setEnableDecoderFallback(true);
        
        // 创建播放器
        player = new ExoPlayer.Builder(this, renderersFactory)
            .setBandwidthMeter(bandwidthMeter)
            .setMediaSourceFactory(
                new DefaultMediaSourceFactory(this)
                    .setDataSourceFactory(dataSourceFactory)
            )
            .build();
        
        // 设置媒体项
        MediaItem mediaItem = new MediaItem.Builder()
            .setUri("https://.../video.mp4")
            .setLiveConfiguration(
                new MediaItem.LiveConfiguration.Builder()
                    .setTargetOffsetMs(10000)
                    .build())
            .build();
        
        player.setMediaItem(mediaItem);
        player.setPlayWhenReady(true);
        player.prepare();
        
        // 绑定播放器到视图
        playerView.setPlayer(player);
    }
    
    private void setupListeners() {
        // 视频质量选择
        qualityButton.setOnClickListener(v -> {
            TrackSelectionDialogBuilder builder = 
                new TrackSelectionDialogBuilder(this, "选择画质", player, C.TRACK_TYPE_VIDEO);
            builder.build().show();
        });
        
        // 播放速度选择
        speedButton.setOnClickListener(v -> {
            new AlertDialog.Builder(this)
                .setTitle("选择播放速度")
                .setItems(new String[]{"0.5x", "1.0x", "1.5x", "2.0x"}, (dialog, which) -> {
                    float speed = 0.5f + 0.5f * which;
                    player.setPlaybackParameters(new PlaybackParameters(speed));
                })
                .show();
        });
        
        // 播放错误处理
        player.addListener(new Player.Listener() {
            @Override
            public void onPlayerError(PlaybackException error) {
                showErrorDialog(error);
            }
        });
    }
    
    private void showErrorDialog(PlaybackException error) {
        int errorCode = PlaybackException.getErrorCode(error);
        String message;
        
        switch (errorCode) {
            case PlaybackException.ERROR_CODE_IO_NETWORK_CONNECTION_FAILED:
                message = "网络连接失败,请检查网络设置";
                break;
            case PlaybackException.ERROR_CODE_DECODING_FAILED:
                message = "视频解码失败,尝试更换播放模式";
                break;
            default:
                message = "播放错误: " + error.getMessage();
        }
        
        new AlertDialog.Builder(this)
            .setTitle("播放失败")
            .setMessage(message)
            .setPositiveButton("重试", (d, i) -> player.retry())
            .setNegativeButton("取消", null)
            .show();
    }
    
    @Override
    protected void onDestroy() {
        super.onDestroy();
        releasePlayer();
    }
    
    private void releasePlayer() {
        if (player != null) {
            player.release();
            player = null;
        }
    }
}

总结

通过AndroidX Media3的ExoPlayer实现视频播放具有以下优势:

  1. 现代化架构:基于最新的AndroidX Media3库
  2. 强大功能:支持HLS/DASH/DRM等高级特性
  3. 高度可定制:UI、播放控制、缓存等均可定制
  4. 高性能:硬件解码和渲染优化
  5. 良好兼容性:支持广泛的媒体格式

最佳实践建议:

  1. 使用PlayerView作为播放器容器
  2. 合理管理播放器生命周期
  3. 实现错误处理和重试机制
  4. 根据应用场景优化播放配置
  5. 支持后台播放时使用MediaSession

通过本指南,您应该能够实现一个功能完善的网络视频播放器,支持主流的流媒体协议和高级播放功能。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值