对于Exoplayer的AdaptiveTrackSelection,我应该切换到具有多个比特率的单个轨道,而不是切换到具有单独比特率的四个轨道吗?

时间:2018-11-01 04:23:29

标签: android streaming rtmp exoplayer

当前,我有一台服务器,该服务器流传输四个RTMP MediaSource,一个具有720p视频源,一个具有360p视频源,一个具有180p视频源,以及一个仅音频源。如果要切换分辨率,则必须停止ExoPlayer实例,准备要切换到的其他曲目,然后播放。

我用于准备ExoPlayer实例的代码:

    TrackSelection.Factory adaptiveTrackSelectionFactory = new AdaptiveTrackSelection.Factory(bandwidthMeter);
    TrackSelector trackSelector = new DefaultTrackSelector(adaptiveTrackSelectionFactory);

    RtmpDataSourceFactory rtmpDataSourceFactory = new RtmpDataSourceFactory(bandwidthMeter);
    ExtractorsFactory extractorsFactory = new DefaultExtractorsFactory();
    factory = new AVControlExtractorMediaSource.Factory(rtmpDataSourceFactory);
    factory.setExtractorsFactory(extractorsFactory);

    createSource();

    //noinspection deprecation
    mPlayer = ExoPlayerFactory.newSimpleInstance(mActivity, trackSelector, new DefaultLoadControl(
            new DefaultAllocator(true, C.DEFAULT_BUFFER_SEGMENT_SIZE),
            1000,  // min buffer
            2000, // max buffer
            1000, // playback
            1000,   //playback after rebuffer
            DefaultLoadControl.DEFAULT_TARGET_BUFFER_BYTES,
            true
    ));

    vwExoPlayer.setPlayer(mPlayer);

    mPlayer.addAnalyticsListener(mAnalyticsListener);

createSource()为:

private void createSource() {
    factory.setTrackPlaybackFlag(AVControlExtractorMediaSource.PLAYBACK_BOTH_AV);
    mMediaSource180 = factory.createMediaSource(Uri.parse(API.GAME_VIDEO_STREAM_URL_180()));
    mMediaSource180.addEventListener(getHandler(), new MSourceDebuggerListener("GameMediaSource180"));

    mMediaSource360 = factory.createMediaSource(Uri.parse(API.GAME_VIDEO_STREAM_URL_360()));
    mMediaSource360.addEventListener(getHandler(), new MSourceDebuggerListener("GameMediaSource360"));

    mMediaSource720 = factory.createMediaSource(Uri.parse(API.GAME_VIDEO_STREAM_URL_720()));
    mMediaSource720.addEventListener(getHandler(), new MSourceDebuggerListener("GameMediaSource720"));

    factory.setTrackPlaybackFlag(AVControlExtractorMediaSource.PLAYBACK_AUDIO_ONLY);
    mMediaSourceAudio = factory.createMediaSource(Uri.parse(API.GAME_AUDIO_STREAM_URL()));
    mMediaSourceAudio.addEventListener(getHandler(), new MSourceDebuggerListener("GameMediaSourceAudio"));
}

private void releaseSource() {
    mMediaSource180.releaseSource(null);
    mMediaSource360.releaseSource(null);
    mMediaSource720.releaseSource(null);
    mMediaSourceAudio.releaseSource(null);
}

我当前在这两个MediaSources之间切换的代码是:

private void changeTrack(MediaSource source) {
    if (currentMediaSource == source) return;

    try {
        this.currentMediaSource = source;
        mPlayer.stop(true);
        mPlayer.prepare(source, true, true);
        mPlayer.setPlayWhenReady(true);

        if (source == mMediaSourceAudio) {
            if (!audioOnly) {
                try {
                    TransitionManager.beginDelayedTransition(rootView);
                } catch (Exception ignored) {

                }

                layAudioOnly.setVisibility(View.VISIBLE);
                vwExoPlayer.setVisibility(View.INVISIBLE);
                audioOnly = true;

                try {
                    GameQnAFragment fragment = findFragment(GameQnAFragment.class);
                    if (fragment != null) {
                        fragment.signAudioOnly();
                    }
                } catch (Exception e) {
                    Trace.e(e);
                }

                try {
                    GamePollingFragment fragment = findFragment(GamePollingFragment.class);
                    if (fragment != null) {
                        fragment.signAudioOnly();
                    }
                } catch (Exception e) {
                    Trace.e(e);
                }
            }
        } else {
            if (audioOnly) {
                TransitionManager.beginDelayedTransition(rootView);
                layAudioOnly.setVisibility(View.GONE);
                vwExoPlayer.setVisibility(View.VISIBLE);
                audioOnly = false;
            }
        }
    } catch (Exception ignore) {

    }
}

我想在这两个MediaSource之间实现无缝切换,这样我就不必停下来重新准备,但ExoPlayer似乎不支持此功能。

此外,使用以下代码记录每个MediaSource结构:

MappingTrackSelector.MappedTrackInfo info = ((DefaultTrackSelector)trackSelector).getCurrentMappedTrackInfo();
    if(info != null) {
        for (int i = 0; i < info.getRendererCount(); i++) {
            TrackGroupArray trackGroups = info.getTrackGroups(i);
            if (trackGroups.length != 0) {
                for(int j = 0; j < trackGroups.length; j++) {
                    TrackGroup tg = trackGroups.get(j);
                    for(int k = 0; k < tg.length; k++) {
                        Log.i("track_info_"+i+"-"+j+"-"+k, tg.getFormat(k)+"");
                    }
                }
            }
        }
    }

只需为我提供1种视频格式和1种音频格式。

我当前的解决方法是在后台准备另一个ExoPlayer实例,在准备工作完成后用当前实例替换当前正在运行的实例,然后释放旧实例。这在某种程度上减少了MediaSources之间的延迟,但是并不能像Youtube那样实现无缝的分辨率更改。

我应该实现自己的TrackSelector并将所有4个源打包到其中,还是实现另一个可以处理所有4个源的MediaSource,或者我应该告诉维护流的同事切换到一个RTMP MediaSource,带有某种清单,其中列出了AdaptiveTrackSelection可以在它们之间切换的所有分辨率?

1 个答案:

答案 0 :(得分:0)

自适应比特率流传输旨在允许在不同的比特率流之间轻松切换,但是它要求对流进行分段,并且播放器逐段下载视频。

通过这种方式,播放器可以根据当前网络条件(以及设备的显示尺寸和t类型)来决定为下一个段选择哪个比特率。除了不同的比特率和质量,播放器还可以无缝地从一种比特率转换到另一种比特率。

有关更多信息,请参见此处:https://stackoverflow.com/a/42365034/334402

以上所有内容均取决于支持此分段和不同比特率流的传输协议。今天最常见的是HLS和MPEG-DASH。

支持我认为正在寻找的最简单方法是为您的同事提供流,以使用HLS和/或DASH来提供流。

请注意,目前,HLS和DASH都是必需的,因为Apple设备需要HLS,而其他设备往往默认为DASH。传统上,HLS使用TS作为分段中视频的容器,而DASH使用分段的MP4,但是现在两者都可以使用CMAF,后者实质上是分段的MP4。

因此,从理论上讲,现在可以将一套比特率视频用于HLS和DASH-实际上,这将取决于您的内容是否已加密,因为HLS和Apple使用了一种加密模式,而其他人则使用了其他加密模式。过去。现在,这种情况也在发生变化,但是在所有设备都支持新方法之前,还需要花费一些时间,因为所有设备都可以支持相同的加密模式,因此,如果对流进行加密,那么这会增加复杂性。