summaryrefslogtreecommitdiffstats
path: root/mobile/android/exoplayer2/src/main/java/org/mozilla/thirdparty/com/google/android/exoplayer2/decoder/DecoderInputBuffer.java
blob: 254ecfdec89ff82f9db878feb4c7c04142083bef (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
/*
 * Copyright (C) 2016 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.mozilla.thirdparty.com.google.android.exoplayer2.decoder;

import androidx.annotation.IntDef;
import androidx.annotation.Nullable;
import org.mozilla.thirdparty.com.google.android.exoplayer2.C;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.nio.ByteBuffer;
import org.checkerframework.checker.nullness.qual.EnsuresNonNull;

/**
 * Holds input for a decoder.
 */
public class DecoderInputBuffer extends Buffer {

  /**
   * The buffer replacement mode, which may disable replacement. One of {@link
   * #BUFFER_REPLACEMENT_MODE_DISABLED}, {@link #BUFFER_REPLACEMENT_MODE_NORMAL} or {@link
   * #BUFFER_REPLACEMENT_MODE_DIRECT}.
   */
  @Documented
  @Retention(RetentionPolicy.SOURCE)
  @IntDef({
    BUFFER_REPLACEMENT_MODE_DISABLED,
    BUFFER_REPLACEMENT_MODE_NORMAL,
    BUFFER_REPLACEMENT_MODE_DIRECT
  })
  public @interface BufferReplacementMode {}
  /**
   * Disallows buffer replacement.
   */
  public static final int BUFFER_REPLACEMENT_MODE_DISABLED = 0;
  /**
   * Allows buffer replacement using {@link ByteBuffer#allocate(int)}.
   */
  public static final int BUFFER_REPLACEMENT_MODE_NORMAL = 1;
  /**
   * Allows buffer replacement using {@link ByteBuffer#allocateDirect(int)}.
   */
  public static final int BUFFER_REPLACEMENT_MODE_DIRECT = 2;

  /**
   * {@link CryptoInfo} for encrypted data.
   */
  public final CryptoInfo cryptoInfo;

  /** The buffer's data, or {@code null} if no data has been set. */
  @Nullable public ByteBuffer data;

  // TODO: Remove this temporary signaling once end-of-stream propagation for clips using content
  // protection is fixed. See [Internal: b/153326944] for details.
  /**
   * Whether the last attempt to read a sample into this buffer failed due to not yet having the DRM
   * keys associated with the next sample.
   */
  public boolean waitingForKeys;

  /**
   * The time at which the sample should be presented.
   */
  public long timeUs;

  /**
   * Supplemental data related to the buffer, if {@link #hasSupplementalData()} returns true. If
   * present, the buffer is populated with supplemental data from position 0 to its limit.
   */
  @Nullable public ByteBuffer supplementalData;

  @BufferReplacementMode private final int bufferReplacementMode;

  /**
   * Creates a new instance for which {@link #isFlagsOnly()} will return true.
   *
   * @return A new flags only input buffer.
   */
  public static DecoderInputBuffer newFlagsOnlyInstance() {
    return new DecoderInputBuffer(BUFFER_REPLACEMENT_MODE_DISABLED);
  }

  /**
   * @param bufferReplacementMode Determines the behavior of {@link #ensureSpaceForWrite(int)}. One
   *     of {@link #BUFFER_REPLACEMENT_MODE_DISABLED}, {@link #BUFFER_REPLACEMENT_MODE_NORMAL} and
   *     {@link #BUFFER_REPLACEMENT_MODE_DIRECT}.
   */
  public DecoderInputBuffer(@BufferReplacementMode int bufferReplacementMode) {
    this.cryptoInfo = new CryptoInfo();
    this.bufferReplacementMode = bufferReplacementMode;
  }

  /**
   * Clears {@link #supplementalData} and ensures that it's large enough to accommodate {@code
   * length} bytes.
   *
   * @param length The length of the supplemental data that must be accommodated, in bytes.
   */
  @EnsuresNonNull("supplementalData")
  public void resetSupplementalData(int length) {
    if (supplementalData == null || supplementalData.capacity() < length) {
      supplementalData = ByteBuffer.allocate(length);
    } else {
      supplementalData.clear();
    }
  }

  /**
   * Ensures that {@link #data} is large enough to accommodate a write of a given length at its
   * current position.
   *
   * <p>If the capacity of {@link #data} is sufficient this method does nothing. If the capacity is
   * insufficient then an attempt is made to replace {@link #data} with a new {@link ByteBuffer}
   * whose capacity is sufficient. Data up to the current position is copied to the new buffer.
   *
   * @param length The length of the write that must be accommodated, in bytes.
   * @throws IllegalStateException If there is insufficient capacity to accommodate the write and
   *     the buffer replacement mode of the holder is {@link #BUFFER_REPLACEMENT_MODE_DISABLED}.
   */
  @EnsuresNonNull("data")
  public void ensureSpaceForWrite(int length) {
    if (data == null) {
      data = createReplacementByteBuffer(length);
      return;
    }
    // Check whether the current buffer is sufficient.
    int capacity = data.capacity();
    int position = data.position();
    int requiredCapacity = position + length;
    if (capacity >= requiredCapacity) {
      return;
    }
    // Instantiate a new buffer if possible.
    ByteBuffer newData = createReplacementByteBuffer(requiredCapacity);
    newData.order(data.order());
    // Copy data up to the current position from the old buffer to the new one.
    if (position > 0) {
      data.flip();
      newData.put(data);
    }
    // Set the new buffer.
    data = newData;
  }

  /**
   * Returns whether the buffer is only able to hold flags, meaning {@link #data} is null and
   * its replacement mode is {@link #BUFFER_REPLACEMENT_MODE_DISABLED}.
   */
  public final boolean isFlagsOnly() {
    return data == null && bufferReplacementMode == BUFFER_REPLACEMENT_MODE_DISABLED;
  }

  /**
   * Returns whether the {@link C#BUFFER_FLAG_ENCRYPTED} flag is set.
   */
  public final boolean isEncrypted() {
    return getFlag(C.BUFFER_FLAG_ENCRYPTED);
  }

  /**
   * Flips {@link #data} and {@link #supplementalData} in preparation for being queued to a decoder.
   *
   * @see java.nio.Buffer#flip()
   */
  public final void flip() {
    data.flip();
    if (supplementalData != null) {
      supplementalData.flip();
    }
  }

  @Override
  public void clear() {
    super.clear();
    if (data != null) {
      data.clear();
    }
    if (supplementalData != null) {
      supplementalData.clear();
    }
    waitingForKeys = false;
  }

  private ByteBuffer createReplacementByteBuffer(int requiredCapacity) {
    if (bufferReplacementMode == BUFFER_REPLACEMENT_MODE_NORMAL) {
      return ByteBuffer.allocate(requiredCapacity);
    } else if (bufferReplacementMode == BUFFER_REPLACEMENT_MODE_DIRECT) {
      return ByteBuffer.allocateDirect(requiredCapacity);
    } else {
      int currentCapacity = data == null ? 0 : data.capacity();
      throw new IllegalStateException("Buffer too small (" + currentCapacity + " < "
          + requiredCapacity + ")");
    }
  }

}