AAC 音频流可以在 Android 的 VLC 中播放,但不能在 Exoplayer 中播放



我有一个RTMP流,我想使用Exoplayer库在我的应用程序中播放。我的设置如下:

TrackSelector trackSelector = new DefaultTrackSelector();
RtmpDataSourceFactory rtmpDataSourceFactory = new RtmpDataSourceFactory(bandwidthMeter);
ExtractorsFactory extractorsFactory = new DefaultExtractorsFactory();
factory = new ExtractorMediaSource.Factory(rtmpDataSourceFactory);
factory.setExtractorsFactory(extractorsFactory);
createSource();
mPlayer = ExoPlayerFactory.newSimpleInstance(mActivity, trackSelector, new DefaultLoadControl(
new DefaultAllocator(true, C.DEFAULT_BUFFER_SEGMENT_SIZE),
1000,  // min buffer
3000, // max buffer
1000, // playback
2000,   //playback after rebuffer
DefaultLoadControl.DEFAULT_TARGET_BUFFER_BYTES,
true
));
vwExoPlayer.setPlayer(mPlayer);
mPlayer.addListener(mVideoStreamHandler);
mPlayer.addVideoListener(new VideoListener() {
@Override
public void onVideoSizeChanged(int width, int height, int unappliedRotationDegrees, float pixelWidthHeightRatio) {
Log.d("hasil", "onVideoSizeChanged: w:" + width + ", h:" + height);
String res = width + "x" + height;
resolution.setText(res);
}
@Override
public void onRenderedFirstFrame() {
}
});

其中createSource()如下:

private void createSource() {
mMediaSource180 = factory.createMediaSource(Uri.parse(API.GAME_VIDEO_STREAM_URL_180));
mMediaSource360 = factory.createMediaSource(Uri.parse(API.GAME_VIDEO_STREAM_URL_360));
mMediaSource720 = factory.createMediaSource(Uri.parse(API.GAME_VIDEO_STREAM_URL_720));
mMediaSourceAudio = factory.createMediaSource(Uri.parse(API.GAME_AUDIO_STREAM_URL));
}

我目前的问题是只有前三个ExtractorMediaSourceExoplayer中工作正常。该mMediaSourceAudio拒绝在Exoplayer播放,但在安卓的VLC媒体播放器中运行良好。

现在我怀疑该格式是AAC-LTP,或任何需要VLC中可用的编解码器但在默认Android中不可用的AAC变体。但是,我无法访问编码过程,所以我不确定。

如果不是这种情况,那是什么?

编辑:

我一直在调试BandwidthMeter并添加了一个MediaSourceEventListener.当我使用普通视频源时,onDownstreamFormatChanged()会被调用,但当我使用该音频流源时不会调用。

此外,BandwidthMeter工作正常,字节始终在流的所有部分下载,当视频流进入时下载更多字节,但仅在仅音频流中,当我调用mPlayer.getBufferedPosition()时,返回的值始终为 0。此外,当我使用音频流源时,没有调用 OMX 代码 - 没有设置解码器。

我是否看到格式错误的音频流,或者我是否需要更改我的 Exoplayer 的设置?

编辑2:

进一步的调试显示,在所有视频流和音频流中,使用相同的FlvExtractor。即使视频流具有avc视频轨道编码和mp4a-latm音频轨道编码。这正常吗?

事实证明,这是因为流被识别为有两个轨道/sampleQueues。一个音轨和一个空格式的音轨。该空轨道应该是视频轨道,根据流的flvHeader标志,它应该存在。

现在,我通过使用自定义MediaPeriod创建自定义MediaSource来解决此问题。所述自定义MediaPeriod具有将SampleQueue的视频和音频轨道分开的代码,然后在我想播放纯音频流时使用纯音频SampleQueue[]而不是源SampleQueue[]

虽然这给了我另一个担忧:可以做一些事情来更改 rtmp 流中的"有音轨 (flag & 0x04) 和视频轨道 (flag & 0x01)"标志,对吧?

感谢您的评论,我是ExoPlayer的新手。但是您的评论帮助我调试并获得了该问题的多种解决方法。

我尝试使用自定义媒体源和自定义媒体周期来解决此音频问题。我观察到视频格式数据在视频+音频wowza流的情况下出现在音频数据之后,因此函数maybeFinishPrepare()将在调用onPrepared之前等待获取视频和音频格式标签数据,以防首先收到视频标签数据。如果首先收到音频数据,它不会等待并调用 onPrepare()。

通过上述更改,我能够单独播放音频并video_audio wowza 流,其中带有 标签类型的 rtmp 标签标头按视频标签数据的顺序出现,然后是音频数据。

我无法将相同的补丁与 srs 服务器一起使用,以播放具有相同更改的audio_only和video_audio流。 srs 服务器按音频和视频标签数据的顺序给出标签数据,

因此,我在 FlvExtractor 中进一步调试。在readFlvHeader中,我覆盖了hasAudio和hasVideo变量。这些变量将根据前几个 tagHeaders(5 或 6)进行设置。我在循环中使用了 peekFull(在输入上)6 次。在获取 tagType 和 tagDataSize 之后的每个循环中,tagDataSize 用于输入 .advancePeekPosition(),tagType 用于识别 tagData 中是否有音频/视频格式数据。在查看了前 6 个连续的 tagHeader 之后,我能够获得 hasAudio 和 hasVideo 的实际值,并忽略了用于设置这些变量的 flvHeaders.flags。

自定义 FlvExtractor 解决方法,看起来比自定义 MediaSource/MediaPeriod 更干净,因为我们将根据需要创建这些曲目,因为我们正在设置适当的 hasVideo/hasAudio 值。