Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Simplify/allow selection of multiple renderers of the same type in DefaultTrackSelector. #6589

Open
mrj976 opened this issue Oct 27, 2019 · 17 comments

Comments

@mrj976
Copy link

mrj976 commented Oct 27, 2019

I want play multi sound in same time(simultaneously) in exoplayer but it only play mediaSource1 and mediaSource2. I want play all mediaSources at the same time(simultaneously)

My code with exoplayer is:

 Uri uri1 = Uri.parse(videopath);
 Uri uri2 = Uri.parse(audio_a.getAbsolutePath());
 Uri uri3 = Uri.parse(audio_b.getAbsolutePath());
 Uri uri4 = Uri.parse(audio_c.getAbsolutePath());

MediaSource mediaSource1 = buildMediaSource(uri1);
MediaSource mediaSource2 = buildMediaSource(uri2);
MediaSource mediaSource3 = buildMediaSource(uri3);
MediaSource mediaSource4 = buildMediaSource(uri4);

MergingMediaSource mediaSource = new MergingMediaSource(mediaSource1, mediaSource2, mediaSource3, mediaSource4);

exoPlayer.prepare(mediaSource, false, false);

please help me
thanks

@tonihei
Copy link
Collaborator

tonihei commented Oct 31, 2019

Multiple streams with the same type of media require multiple renderers to play them at the same time. By default, ExoPlayer's DefaultRenderersFactory provides one MediaCodecAudioRenderer and potentially some extension audio renderers. So it's actually surprising that two of the audio files play in parallel already.

You could in theory provide multiple MediaCodecAudioRenderers in a RenderersFactory you pass in to ExoPlayerFactory. However, audio renderers also function as the media clock and the player fails if multiple media clocks are provided. You can circumvent this problem by subclassing MediaCodecAudioRenderer and returning null from getMediaClock. I haven't tested this, but this would probably allow the parallel playback. Note however, that there won't be any time synchronization between the renderers and the audio files may drift apart.

@tonihei
Copy link
Collaborator

tonihei commented Oct 31, 2019

If your app is doing nothing else with ExoPlayer and all the files are local, you can also consider using SoundPool without any ExoPlayer integration.

@mrj976
Copy link
Author

mrj976 commented Nov 1, 2019

Multiple streams with the same type of media require multiple renderers to play them at the same time. By default, ExoPlayer's DefaultRenderersFactory provides one MediaCodecAudioRenderer and potentially some extension audio renderers. So it's actually surprising that two of the audio files play in parallel already.

You could in theory provide multiple MediaCodecAudioRenderers in a RenderersFactory you pass in to ExoPlayerFactory. However, audio renderers also function as the media clock and the player fails if multiple media clocks are provided. You can circumvent this problem by subclassing MediaCodecAudioRenderer and returning null from getMediaClock. I haven't tested this, but this would probably allow the parallel playback. Note however, that there won't be any time synchronization between the renderers and the audio files may drift apart.

Thanks
I have 6 audio files
Can you send me an example?

@mrj976
Copy link
Author

mrj976 commented Nov 1, 2019

If your app is doing nothing else with ExoPlayer and all the files are local, you can also consider using SoundPool without any ExoPlayer integration.

I want play a video with 6 audio files
So I used ExoPlayer
Is there any other way to solve this problem?
Use another library

@tonihei
Copy link
Collaborator

tonihei commented Nov 5, 2019

The way I described above seems to work with the added complication of needing a custom TrackSelector implementation that links tracks to audio renderers.

Please note that the renderers are not actively synchronized to each other, so depending on your alignment requirements between these streams, this may be a problem. You will also need to select the video track in the track selector if you want to enable it.

Code I used for testing:

-----MediaSource-----
mediaSource = new MergingMediaSource(source1, ..., source4);

----RendersFactory-----
RenderersFactory renderersFactory = new DefaultRenderersFactory(this) {
  @Override
  protected void buildAudioRenderers(Context context, int extensionRendererMode,
      MediaCodecSelector mediaCodecSelector,
      @Nullable DrmSessionManager<FrameworkMediaCrypto> drmSessionManager,
      boolean playClearSamplesWithoutKeys, boolean enableDecoderFallback,
      AudioProcessor[] audioProcessors, Handler eventHandler,
      AudioRendererEventListener eventListener, ArrayList<Renderer> out) {
        super.buildAudioRenderers(context, extensionRendererMode, mediaCodecSelector,
          drmSessionManager, playClearSamplesWithoutKeys, enableDecoderFallback,
          audioProcessors, eventHandler, eventListener, out);
        out.add(new AudioRendererWithoutClock(context, mediaCodecSelector));
        out.add(new AudioRendererWithoutClock(context, mediaCodecSelector));
        out.add(new AudioRendererWithoutClock(context, mediaCodecSelector));
    }
 };

with the following class:

private static final class AudioRendererWithoutClock extends MediaCodecAudioRenderer {
    public AudioRendererWithoutClock(Context context,
        MediaCodecSelector mediaCodecSelector) {
      super(context, mediaCodecSelector);
    }
    @Override
    public MediaClock getMediaClock() {
      return null;
    }
}

---- TrackSelector ----
TrackSelector trackSelector = new TrackSelector() {
    @Override
    public TrackSelectorResult selectTracks(RendererCapabilities[] rendererCapabilities,
        TrackGroupArray trackGroups, MediaPeriodId periodId, Timeline timeline)
        throws ExoPlaybackException {
      Queue<Integer> audioRenderers = new ArrayDeque<>();
      RendererConfiguration[] configs = new RendererConfiguration[rendererCapabilities.length];
      TrackSelection[] selections = new TrackSelection[rendererCapabilities.length];
      for (int i = 0; i < rendererCapabilities.length; i++) {
        if(rendererCapabilities[i].getTrackType() == C.TRACK_TYPE_AUDIO) {
          audioRenderers.add(i);
        }
      }
      for (int i = 0; i < trackGroups.length; i++) {
        if (MimeTypes.isAudio(trackGroups.get(i).getFormat(0).sampleMimeType)) {
          Integer index = audioRenderers.poll();
          if (index != null) {
            selections[index] = new FixedTrackSelection(trackGroups.get(i), 0);
            configs[index] = RendererConfiguration.DEFAULT;
          }
        }
      }
      return new TrackSelectorResult(configs, selections, new Object());
    }

    @Override
    public void onSelectionActivated(Object info) {
     }
 };

---- Player ----
player =
      new SimpleExoPlayer.Builder(/* context= */ this, renderersFactory)
         .setTrackSelector(trackSelector)
         .build();
player.prepare(mediaSource);

@mrj976
Copy link
Author

mrj976 commented Nov 7, 2019

The way I described above seems to work with the added complication of needing a custom TrackSelector implementation that links tracks to audio renderers.

Please note that the renderers are not actively synchronized to each other, so depending on your alignment requirements between these streams, this may be a problem. You will also need to select the video track in the track selector if you want to enable it.

Code I used for testing:

-----MediaSource-----
mediaSource = new MergingMediaSource(source1, ..., source4);

----RendersFactory-----
RenderersFactory renderersFactory = new DefaultRenderersFactory(this) {
  @Override
  protected void buildAudioRenderers(Context context, int extensionRendererMode,
      MediaCodecSelector mediaCodecSelector,
      @Nullable DrmSessionManager<FrameworkMediaCrypto> drmSessionManager,
      boolean playClearSamplesWithoutKeys, boolean enableDecoderFallback,
      AudioProcessor[] audioProcessors, Handler eventHandler,
      AudioRendererEventListener eventListener, ArrayList<Renderer> out) {
        super.buildAudioRenderers(context, extensionRendererMode, mediaCodecSelector,
          drmSessionManager, playClearSamplesWithoutKeys, enableDecoderFallback,
          audioProcessors, eventHandler, eventListener, out);
        out.add(new AudioRendererWithoutClock(context, mediaCodecSelector));
        out.add(new AudioRendererWithoutClock(context, mediaCodecSelector));
        out.add(new AudioRendererWithoutClock(context, mediaCodecSelector));
    }
 };

with the following class:

private static final class AudioRendererWithoutClock extends MediaCodecAudioRenderer {
    public AudioRendererWithoutClock(Context context,
        MediaCodecSelector mediaCodecSelector) {
      super(context, mediaCodecSelector);
    }
    @Override
    public MediaClock getMediaClock() {
      return null;
    }
}

---- TrackSelector ----
TrackSelector trackSelector = new TrackSelector() {
    @Override
    public TrackSelectorResult selectTracks(RendererCapabilities[] rendererCapabilities,
        TrackGroupArray trackGroups, MediaPeriodId periodId, Timeline timeline)
        throws ExoPlaybackException {
      Queue<Integer> audioRenderers = new ArrayDeque<>();
      RendererConfiguration[] configs = new RendererConfiguration[rendererCapabilities.length];
      TrackSelection[] selections = new TrackSelection[rendererCapabilities.length];
      for (int i = 0; i < rendererCapabilities.length; i++) {
        if(rendererCapabilities[i].getTrackType() == C.TRACK_TYPE_AUDIO) {
          audioRenderers.add(i);
          configs[i] = RendererConfiguration.DEFAULT;
        }
      }
      for (int i = 0; i < trackGroups.length; i++) {
        if (MimeTypes.isAudio(trackGroups.get(i).getFormat(0).sampleMimeType)) {
          Integer index = audioRenderers.poll();
          if (index != null) {
            selections[index] = new FixedTrackSelection(trackGroups.get(i), 0);
          }
        }
      }
      return new TrackSelectorResult(configs, selections, new Object());
    }

    @Override
    public void onSelectionActivated(Object info) {
     }
 };

---- Player ----
player =
      new SimpleExoPlayer.Builder(/* context= */ this, renderersFactory)
         .setTrackSelector(trackSelector)
         .build();
player.prepare(mediaSource);

Thank you
I used your code but there is error in this section:

player =
      new SimpleExoPlayer.Builder(/* context= */ this, renderersFactory)
         .setTrackSelector(trackSelector)
         .build();

In above code ".Builder" is red and show this message: " Cannot resolve symbol 'Builder' "

In my MediaSources:
mediasource1 is video file
mediasource2 , mediasource3 , mediasource4, mediasource5, mediasource6, mediasource7 are audio files

what should I do?

@tonihei
Copy link
Collaborator

tonihei commented Nov 7, 2019

The builder is only available in the latest ExoPlayer version. You can use ExoPlayerFactory instead where you can set your own track selector and renderers factory.

@mrj976
Copy link
Author

mrj976 commented Nov 9, 2019

The builder is only available in the latest ExoPlayer version. You can use ExoPlayerFactory instead where you can set your own track selector and renderers factory.

Thank you for your sample code and your help
I used exoplayer with 2.10.7 version and its latest version of exoplayer. but show this message: " Cannot resolve symbol 'Builder' " again.
implementation 'com.google.android.exoplayer:exoplayer:2.10.7'
I solved this error with this code:
Player = ExoPlayerFactory.newSimpleInstance(this, renderersFactory, trackSelector);

with your code i can play multiple audio files but dont show video.

My code in media source dection:

Uri uri1 = Uri.parse(video_path);
        Uri uri2 = Uri.parse(audio_path1);
        Uri uri3 = Uri.parse(audio_path2);
        Uri uri4 = Uri.parse(audio_path3);

        MediaSource mediaSource1 = buildMediaSource(uri1);
        MediaSource mediaSource2 = buildMediaSource(uri2);
        MediaSource mediaSource3 = buildMediaSource(uri3);
        MediaSource mediaSource4 = buildMediaSource(uri4);

        MergingMediaSource mediaSource = new MergingMediaSource(mediaSource1, mediaSource2, mediaSource3, mediaSource4);

what should I do for play video with this audio files in same time?

@tonihei
Copy link
Collaborator

tonihei commented Nov 11, 2019

I used exoplayer with 2.10.7 version and its latest version of exoplayer. but show this message: " Cannot resolve symbol 'Builder' " again.

Yes, sorry, this will only be part of 2.11.0 (once released). Your alternative is correct.

i can play multiple audio files but dont show video

As pointed out above, you also need to select the video track in the TrackSelector implementation. Search for a renderer index that supports C.TRACK_TYPE_VIDEO and find a track where MimeTypes.isVideo returns true, and then create a new FixedTrackSelection similar to what I've done with the audio tracks in my example.

Note that writing you own TrackSelector is quite an advanced customization, so I won't dive any deeper with code examples.

complication of needing a custom TrackSelector

This turned out to be quite complicated and I'll use this issue to track an enhancement for DefaultTrackSelector that allows selecting multiple renderers of the same type (e.g. multiple video outputs, multiple audio renderers, etc.). But it's low-priority for now.

@tonihei tonihei changed the title play Multi audio files simultaneously in exoplayer? Simplify/allow selection of multiple renderers of the same type in DefaultTrackSelector. Nov 11, 2019
@tonihei tonihei changed the title Simplify/allow selection of multiple renderers of the same type in DefaultTrackSelector. Simplify/allow selection of multiple renderers of the same type in DefaultTrackSelector. Nov 11, 2019
@kim-vde kim-vde closed this as completed Jan 9, 2020
@kim-vde kim-vde reopened this Jan 9, 2020
@mrj976

This comment has been minimized.

@mrj976

This comment has been minimized.

@bugsCreator

This comment has been minimized.

@wangxuan217
Copy link

The way I described above seems to work with the added complication of needing a custom TrackSelector implementation that links tracks to audio renderers.
Please note that the renderers are not actively synchronized to each other, so depending on your alignment requirements between these streams, this may be a problem. You will also need to select the video track in the track selector if you want to enable it.
Code I used for testing:

-----MediaSource-----
mediaSource = new MergingMediaSource(source1, ..., source4);

----RendersFactory-----
RenderersFactory renderersFactory = new DefaultRenderersFactory(this) {
  @Override
  protected void buildAudioRenderers(Context context, int extensionRendererMode,
      MediaCodecSelector mediaCodecSelector,
      @Nullable DrmSessionManager<FrameworkMediaCrypto> drmSessionManager,
      boolean playClearSamplesWithoutKeys, boolean enableDecoderFallback,
      AudioProcessor[] audioProcessors, Handler eventHandler,
      AudioRendererEventListener eventListener, ArrayList<Renderer> out) {
        super.buildAudioRenderers(context, extensionRendererMode, mediaCodecSelector,
          drmSessionManager, playClearSamplesWithoutKeys, enableDecoderFallback,
          audioProcessors, eventHandler, eventListener, out);
        out.add(new AudioRendererWithoutClock(context, mediaCodecSelector));
        out.add(new AudioRendererWithoutClock(context, mediaCodecSelector));
        out.add(new AudioRendererWithoutClock(context, mediaCodecSelector));
    }
 };

with the following class:

private static final class AudioRendererWithoutClock extends MediaCodecAudioRenderer {
    public AudioRendererWithoutClock(Context context,
        MediaCodecSelector mediaCodecSelector) {
      super(context, mediaCodecSelector);
    }
    @Override
    public MediaClock getMediaClock() {
      return null;
    }
}

---- TrackSelector ----
TrackSelector trackSelector = new TrackSelector() {
    @Override
    public TrackSelectorResult selectTracks(RendererCapabilities[] rendererCapabilities,
        TrackGroupArray trackGroups, MediaPeriodId periodId, Timeline timeline)
        throws ExoPlaybackException {
      Queue<Integer> audioRenderers = new ArrayDeque<>();
      RendererConfiguration[] configs = new RendererConfiguration[rendererCapabilities.length];
      TrackSelection[] selections = new TrackSelection[rendererCapabilities.length];
      for (int i = 0; i < rendererCapabilities.length; i++) {
        if(rendererCapabilities[i].getTrackType() == C.TRACK_TYPE_AUDIO) {
          audioRenderers.add(i);
          configs[i] = RendererConfiguration.DEFAULT;
        }
      }
      for (int i = 0; i < trackGroups.length; i++) {
        if (MimeTypes.isAudio(trackGroups.get(i).getFormat(0).sampleMimeType)) {
          Integer index = audioRenderers.poll();
          if (index != null) {
            selections[index] = new FixedTrackSelection(trackGroups.get(i), 0);
          }
        }
      }
      return new TrackSelectorResult(configs, selections, new Object());
    }

    @Override
    public void onSelectionActivated(Object info) {
     }
 };

---- Player ----
player =
      new SimpleExoPlayer.Builder(/* context= */ this, renderersFactory)
         .setTrackSelector(trackSelector)
         .build();
player.prepare(mediaSource);

Thank you
I used your code but there is error in this section:

player =
      new SimpleExoPlayer.Builder(/* context= */ this, renderersFactory)
         .setTrackSelector(trackSelector)
         .build();

In above code ".Builder" is red and show this message: " Cannot resolve symbol 'Builder' "

In my MediaSources:
mediasource1 is video file
mediasource2 , mediasource3 , mediasource4, mediasource5, mediasource6, mediasource7 are audio files

what should I do?

Can I switch between the video track and the audio track?

@ziad-halabi9
Copy link

I had the same code suggested here in order to play multiple tracks at the same time. But now after updating to ExoPlayer 2.17, it stopped working. The following is the error I can see:

E/ExoPlayerImplInternal: Playback error
      com.google.android.exoplayer2.ExoPlaybackException: Unexpected runtime error
        at com.google.android.exoplayer2.ExoPlayerImplInternal.handleMessage(ExoPlayerImplInternal.java:624)
        at android.os.Handler.dispatchMessage(Handler.java:103)
        at android.os.Looper.loop(Looper.java:237)
        at android.os.HandlerThread.run(HandlerThread.java:67)
     Caused by: java.lang.IllegalStateException
        at com.google.android.exoplayer2.util.Assertions.checkState(Assertions.java:84)
        at com.google.android.exoplayer2.source.ProgressiveMediaPeriod.selectTracks(ProgressiveMediaPeriod.java:283)
        at com.google.android.exoplayer2.source.MergingMediaPeriod.selectTracks(MergingMediaPeriod.java:153)
        at com.google.android.exoplayer2.source.MaskingMediaPeriod.selectTracks(MaskingMediaPeriod.java:186)
        at com.google.android.exoplayer2.MediaPeriodHolder.applyTrackSelection(MediaPeriodHolder.java:296)
        at com.google.android.exoplayer2.MediaPeriodHolder.applyTrackSelection(MediaPeriodHolder.java:259)
        at com.google.android.exoplayer2.MediaPeriodHolder.handlePrepared(MediaPeriodHolder.java:193)
        at com.google.android.exoplayer2.ExoPlayerImplInternal.handlePeriodPrepared(ExoPlayerImplInternal.java:2247)
        at com.google.android.exoplayer2.ExoPlayerImplInternal.handleMessage(ExoPlayerImplInternal.java:517)
        at android.os.Handler.dispatchMessage(Handler.java:103) 
        at android.os.Looper.loop(Looper.java:237) 
        at android.os.HandlerThread.run(HandlerThread.java:67) 

Any suggestions on what can be causing this and if there's a quick fix?

@Brutalnung
Copy link

Brutalnung commented Mar 10, 2022 via email

@Brutalnung
Copy link

E/ExoPlayerImplInternal: Playback error
com.google.android.exoplayer2.ExoPlaybackException: Unexpected runtime error
at com.google.android.exoplayer2.ExoPlayerImplInternal.handleMessage(ExoPlayerImplInternal.java:624)
at android.os.Handler.dispatchMessage(Handler.java:103)
at android.os.Looper.loop(Looper.java:237)
at android.os.HandlerThread.run(HandlerThread.java:67)
Caused by: java.lang.IllegalStateException
at com.google.android.exoplayer2.util.Assertions.checkState(Assertions.java:84)
at com.google.android.exoplayer2.source.ProgressiveMediaPeriod.selectTracks(ProgressiveMediaPeriod.java:283)
at com.google.android.exoplayer2.source.MergingMediaPeriod.selectTracks(MergingMediaPeriod.java:153)
at com.google.android.exoplayer2.source.MaskingMediaPeriod.selectTracks(MaskingMediaPeriod.java:186)
at com.google.android.exoplayer2.MediaPeriodHolder.applyTrackSelection(MediaPeriodHolder.java:296)
at com.google.android.exoplayer2.MediaPeriodHolder.applyTrackSelection(MediaPeriodHolder.java:259)
at com.google.android.exoplayer2.MediaPeriodHolder.handlePrepared(MediaPeriodHolder.java:193)
at com.google.android.exoplayer2.ExoPlayerImplInternal.handlePeriodPrepared(ExoPlayerImplInternal.java:2247)
at com.google.android.exoplayer2.ExoPlayerImplInternal.handleMessage(ExoPlayerImplInternal.java:517)
at android.os.Handler.dispatchMessage(Handler.java:103) 
at android.os.Looper.loop(Looper.java:237) 
at android.os.HandlerThread.run(HandlerThread.java:67) 

@Brutalnung
Copy link

ขอคำชี้แจง

y4n9b0 pushed a commit to y4n9b0/ExoPlayer that referenced this issue Jun 2, 2022
多音轨支持的一般思路:
1. 自定义 AudioRenderer 和 RendererFactory,创建多个 audio renderer
2. 自定义 TrackSelector,建立 track 和 renderer 之间的映射关系
3. build ExoPlayer 时设置自定义的 RendererFactory 和 TrackSelector
参考 google#6589

此外,ExoPlayer 音频播放使用系统 media 的 AudioTrack,在版本 2.13.0 时,
ExoPlayer 变更了 audioSessionId 的生成方式和时机,导致多个音轨 renderer
对应了同一个 audioSessionId 的 AudioTrack,从而播放异常。
所以,此版本 ExoPlayer 的多音轨支持基于上述思路又额外重构了 audioSessionId
的生成方式。
ExoPlayer audioSessionId 相关变动参考:
google@a95b2eb

note for dev:
```kotlin
val renderersFactory = MultiAudioRenderersFactory(
    context,
    audioRenderersCount = 3,
    mediaClockRendererIndex = 0
)
val exoPlayer = ExoPlayer.Builder(context)
    .setRenderersFactory(renderersFactory)
    .build()
```
y4n9b0 pushed a commit to y4n9b0/ExoPlayer that referenced this issue Jun 2, 2022
多音轨支持的一般思路:
1. 自定义 AudioRenderer 和 RendererFactory,创建多个 audio renderer
2. 自定义 TrackSelector,建立 track 和 renderer 之间的映射关系
3. build ExoPlayer 时设置自定义的 RendererFactory 和 TrackSelector
参考 google#6589

此外,ExoPlayer 音频播放使用系统 media 的 AudioTrack,在版本 2.13.0 时,
ExoPlayer 变更了 audioSessionId 的生成方式和时机,导致多个音轨 renderer
对应了同一个 audioSessionId 的 AudioTrack,从而播放异常。
所以,此版本 ExoPlayer 的多音轨支持基于上述思路又额外重构了 audioSessionId
的生成方式。
ExoPlayer audioSessionId 相关变动参考:
google@a95b2eb

note for dev:
```kotlin
val renderersFactory = MultiAudioRenderersFactory(
    context,
    audioRenderersCount = 3,
    mediaClockRendererIndex = 0
)
val exoPlayer = ExoPlayer.Builder(context)
    .setRenderersFactory(renderersFactory)
    .build()
```
y4n9b0 pushed a commit to y4n9b0/ExoPlayer that referenced this issue Jun 2, 2022
多音轨支持的一般思路:
1. 自定义 AudioRenderer 和 RendererFactory,创建多个 audio renderer
2. 自定义 TrackSelector,建立 track 和 renderer 之间的映射关系
3. build ExoPlayer 时设置自定义的 RendererFactory 和 TrackSelector
参考 google#6589

此外,ExoPlayer 音频播放使用系统 media 的 AudioTrack,在版本 2.13.0 时,
ExoPlayer 变更了 audioSessionId 的生成方式和时机,导致多个音轨 renderer
对应了同一个 audioSessionId 的 AudioTrack,从而播放异常。
所以,此版本 ExoPlayer 的多音轨支持基于上述思路又额外重构了 audioSessionId
的生成方式。
ExoPlayer audioSessionId 相关变动参考:
google@a95b2eb

note for dev:
```kotlin
val renderersFactory = MultiAudioRenderersFactory(
    context,
    audioRenderersCount = 2,
    mediaClockRendererIndex = 0
)
val exoPlayer = ExoPlayer.Builder(context)
    .setRenderersFactory(renderersFactory)
    .build()
```
y4n9b0 pushed a commit to y4n9b0/ExoPlayer that referenced this issue Jun 2, 2022
多音轨支持的一般思路:
1. 自定义 AudioRenderer 和 RendererFactory,创建多个 audio renderer
2. 自定义 TrackSelector,建立 track 和 renderer 之间的映射关系
3. build ExoPlayer 时设置自定义的 RendererFactory 和 TrackSelector
参考 google#6589

此外,ExoPlayer 音频播放使用系统 media 的 AudioTrack,在版本 2.13.0 时,
ExoPlayer 变更了 audioSessionId 的生成方式和时机,导致多个音轨 renderer
对应了同一个 audioSessionId 的 AudioTrack,从而播放异常。
所以,此版本 ExoPlayer 的多音轨支持基于上述思路又额外重构了 audioSessionId
的生成方式。
ExoPlayer audioSessionId 相关变动参考:
google@a95b2eb

note for dev:
```kotlin
val renderersFactory = MultiAudioRenderersFactory(
    context,
    audioRenderersCount = 2,
    mediaClockRendererIndex = 0
)
val exoPlayer = ExoPlayer.Builder(context)
    .setRenderersFactory(renderersFactory)
    .build()
```
y4n9b0 pushed a commit to y4n9b0/ExoPlayer that referenced this issue Jun 8, 2022
多音轨支持的一般思路:
1. 自定义 AudioRenderer 和 RendererFactory,创建多个 audio renderer
2. 自定义 TrackSelector,建立 track 和 renderer 之间的映射关系
3. build ExoPlayer 时设置自定义的 RendererFactory 和 TrackSelector
参考 google#6589

此外,ExoPlayer 音频播放使用系统 media 的 AudioTrack,在版本 2.13.0 时,
ExoPlayer 变更了 audioSessionId 的生成方式和时机,导致多个音轨 renderer
对应了同一个 audioSessionId 的 AudioTrack,从而播放异常。
所以,此版本 ExoPlayer 的多音轨支持基于上述思路又额外重构了 audioSessionId
的生成方式。
ExoPlayer audioSessionId 相关变动参考:
google@a95b2eb

note for dev:
```kotlin
val renderersFactory = MultiAudioRenderersFactory(
    context,
    audioRenderersCount = 2,
    mediaClockRendererIndex = 0
)
val exoPlayer = ExoPlayer.Builder(context)
    .setRenderersFactory(renderersFactory)
    .build()
```
y4n9b0 pushed a commit to y4n9b0/ExoPlayer that referenced this issue Jun 8, 2022
多音轨支持的一般思路:
1. 自定义 AudioRenderer 和 RendererFactory,创建多个 audio renderer
2. 自定义 TrackSelector,建立 track 和 renderer 之间的映射关系
3. build ExoPlayer 时设置自定义的 RendererFactory 和 TrackSelector
参考 google#6589 (comment)

此外,ExoPlayer 音频播放使用系统 media 的 AudioTrack,在版本 2.13.0 时,
ExoPlayer 变更了 audioSessionId 的生成方式和时机,导致多个音轨 renderer
对应了同一个 audioSessionId 的 AudioTrack,从而播放异常。
所以,此版本 ExoPlayer 的多音轨支持基于上述思路又额外重构了 audioSessionId
的生成方式。
ExoPlayer audioSessionId 相关变动参考:
google@a95b2eb

note for dev:
```kotlin
val renderersFactory = MultiAudioRenderersFactory(
    context,
    audioRenderersCount = 2,
    mediaClockRendererIndex = 0
)
val exoPlayer = ExoPlayer.Builder(context)
    .setRenderersFactory(renderersFactory)
    .build()
```
y4n9b0 pushed a commit to y4n9b0/ExoPlayer that referenced this issue Jun 8, 2022
多音轨支持的一般思路:
1. 自定义 AudioRenderer 和 RendererFactory,创建多个 audio renderer
2. 自定义 TrackSelector,建立 track 和 renderer 之间的映射关系
3. build ExoPlayer 时设置自定义的 RendererFactory 和 TrackSelector
参考 http$://github.com/google/issues/6589#issuecomment-549864783

此外,ExoPlayer 音频播放使用系统 media 的 AudioTrack,在版本 2.13.0 时,
ExoPlayer 变更了 audioSessionId 的生成方式和时机,导致多个音轨 renderer
对应了同一个 audioSessionId 的 AudioTrack,从而播放异常。
所以,此版本 ExoPlayer 的多音轨支持基于上述思路又额外重构了 audioSessionId
的生成方式。
ExoPlayer audioSessionId 相关变动参考:
google@a95b2eb

note for dev:
```kotlin
val renderersFactory = MultiAudioRenderersFactory(
    context,
    audioRenderersCount = 2,
    mediaClockRendererIndex = 0
)
val exoPlayer = ExoPlayer.Builder(context)
    .setRenderersFactory(renderersFactory)
    .build()
```
y4n9b0 pushed a commit to y4n9b0/ExoPlayer that referenced this issue Jun 8, 2022
多音轨支持的一般思路:
1. 自定义 AudioRenderer 和 RendererFactory,创建多个 audio renderer
2. 自定义 TrackSelector,建立 track 和 renderer 之间的映射关系
3. build ExoPlayer 时设置自定义的 RendererFactory 和 TrackSelector
参考 google#6589 (comment)

此外,ExoPlayer 音频播放使用系统 media 的 AudioTrack,在版本 2.13.0 时,
ExoPlayer 变更了 audioSessionId 的生成方式和时机,导致多个音轨 renderer
对应了同一个 audioSessionId 的 AudioTrack,从而播放异常。
所以,此版本 ExoPlayer 的多音轨支持基于上述思路又额外重构了 audioSessionId
的生成方式。
ExoPlayer audioSessionId 相关变动参考:
google@a95b2eb

note for dev:
```kotlin
val renderersFactory = MultiAudioRenderersFactory(
    context,
    audioRenderersCount = 2,
    mediaClockRendererIndex = 0
)
val exoPlayer = ExoPlayer.Builder(context)
    .setRenderersFactory(renderersFactory)
    .build()
```
y4n9b0 pushed a commit to y4n9b0/ExoPlayer that referenced this issue Feb 22, 2023
多音轨支持的一般思路:
1. 自定义 AudioRenderer 和 RendererFactory,创建多个 audio renderer
2. 自定义 TrackSelector,建立 track 和 renderer 之间的映射关系
3. build ExoPlayer 时设置自定义的 RendererFactory 和 TrackSelector
参考 google#6589 (comment)

此外,ExoPlayer 音频播放使用系统 media 的 AudioTrack,在版本 2.13.0 时,
ExoPlayer 变更了 audioSessionId 的生成方式和时机,导致多个音轨 renderer
对应了同一个 audioSessionId 的 AudioTrack,从而播放异常。
所以,此版本 ExoPlayer 的多音轨支持基于上述思路又额外重构了 audioSessionId
的生成方式。
ExoPlayer audioSessionId 相关变动参考:
google@a95b2eb

note for dev:
```kotlin
val renderersFactory = MultiAudioRenderersFactory(
    context,
    audioRenderersCount = 2,
    mediaClockRendererIndex = 0
)
val exoPlayer = ExoPlayer.Builder(context)
    .setRenderersFactory(renderersFactory)
    .build()
```
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

7 participants