• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.google.android.exoplayer2.audio;
17 
18 import androidx.annotation.Nullable;
19 import com.google.android.exoplayer2.C;
20 import com.google.android.exoplayer2.util.Assertions;
21 import java.nio.ByteBuffer;
22 
23 /**
24  * An {@link AudioProcessor} that applies a mapping from input channels onto specified output
25  * channels. This can be used to reorder, duplicate or discard channels.
26  */
27 /* package */ final class ChannelMappingAudioProcessor extends BaseAudioProcessor {
28 
29   @Nullable private int[] pendingOutputChannels;
30   @Nullable private int[] outputChannels;
31 
32   /**
33    * Resets the channel mapping. After calling this method, call {@link #configure(AudioFormat)} to
34    * start using the new channel map.
35    *
36    * @param outputChannels The mapping from input to output channel indices, or {@code null} to
37    *     leave the input unchanged.
38    * @see AudioSink#configure(int, int, int, int, int[], int, int)
39    */
setChannelMap(@ullable int[] outputChannels)40   public void setChannelMap(@Nullable int[] outputChannels) {
41     pendingOutputChannels = outputChannels;
42   }
43 
44   @Override
onConfigure(AudioFormat inputAudioFormat)45   public AudioFormat onConfigure(AudioFormat inputAudioFormat)
46       throws UnhandledAudioFormatException {
47     @Nullable int[] outputChannels = pendingOutputChannels;
48     if (outputChannels == null) {
49       return AudioFormat.NOT_SET;
50     }
51 
52     if (inputAudioFormat.encoding != C.ENCODING_PCM_16BIT) {
53       throw new UnhandledAudioFormatException(inputAudioFormat);
54     }
55 
56     boolean active = inputAudioFormat.channelCount != outputChannels.length;
57     for (int i = 0; i < outputChannels.length; i++) {
58       int channelIndex = outputChannels[i];
59       if (channelIndex >= inputAudioFormat.channelCount) {
60         throw new UnhandledAudioFormatException(inputAudioFormat);
61       }
62       active |= (channelIndex != i);
63     }
64     return active
65         ? new AudioFormat(inputAudioFormat.sampleRate, outputChannels.length, C.ENCODING_PCM_16BIT)
66         : AudioFormat.NOT_SET;
67   }
68 
69   @Override
queueInput(ByteBuffer inputBuffer)70   public void queueInput(ByteBuffer inputBuffer) {
71     int[] outputChannels = Assertions.checkNotNull(this.outputChannels);
72     int position = inputBuffer.position();
73     int limit = inputBuffer.limit();
74     int frameCount = (limit - position) / inputAudioFormat.bytesPerFrame;
75     int outputSize = frameCount * outputAudioFormat.bytesPerFrame;
76     ByteBuffer buffer = replaceOutputBuffer(outputSize);
77     while (position < limit) {
78       for (int channelIndex : outputChannels) {
79         buffer.putShort(inputBuffer.getShort(position + 2 * channelIndex));
80       }
81       position += inputAudioFormat.bytesPerFrame;
82     }
83     inputBuffer.position(limit);
84     buffer.flip();
85   }
86 
87   @Override
onFlush()88   protected void onFlush() {
89     outputChannels = pendingOutputChannels;
90   }
91 
92   @Override
onReset()93   protected void onReset() {
94     outputChannels = null;
95     pendingOutputChannels = null;
96   }
97 
98 }
99