1 /* 2 * Copyright (C) 2016 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; 17 18 import android.os.SystemClock; 19 import android.text.TextUtils; 20 import androidx.annotation.IntDef; 21 import androidx.annotation.Nullable; 22 import com.google.android.exoplayer2.RendererCapabilities.FormatSupport; 23 import com.google.android.exoplayer2.source.MediaSource; 24 import com.google.android.exoplayer2.util.Assertions; 25 import java.io.IOException; 26 import java.lang.annotation.Documented; 27 import java.lang.annotation.Retention; 28 import java.lang.annotation.RetentionPolicy; 29 30 /** 31 * Thrown when a non-recoverable playback failure occurs. 32 */ 33 public final class ExoPlaybackException extends Exception { 34 35 /** 36 * The type of source that produced the error. One of {@link #TYPE_SOURCE}, {@link #TYPE_RENDERER} 37 * {@link #TYPE_UNEXPECTED}, {@link #TYPE_REMOTE} or {@link #TYPE_OUT_OF_MEMORY}. Note that new 38 * types may be added in the future and error handling should handle unknown type values. 39 */ 40 @Documented 41 @Retention(RetentionPolicy.SOURCE) 42 @IntDef({TYPE_SOURCE, TYPE_RENDERER, TYPE_UNEXPECTED, TYPE_REMOTE, TYPE_OUT_OF_MEMORY}) 43 public @interface Type {} 44 /** 45 * The error occurred loading data from a {@link MediaSource}. 46 * <p> 47 * Call {@link #getSourceException()} to retrieve the underlying cause. 48 */ 49 public static final int TYPE_SOURCE = 0; 50 /** 51 * The error occurred in a {@link Renderer}. 52 * <p> 53 * Call {@link #getRendererException()} to retrieve the underlying cause. 54 */ 55 public static final int TYPE_RENDERER = 1; 56 /** 57 * The error was an unexpected {@link RuntimeException}. 58 * <p> 59 * Call {@link #getUnexpectedException()} to retrieve the underlying cause. 60 */ 61 public static final int TYPE_UNEXPECTED = 2; 62 /** 63 * The error occurred in a remote component. 64 * 65 * <p>Call {@link #getMessage()} to retrieve the message associated with the error. 66 */ 67 public static final int TYPE_REMOTE = 3; 68 /** The error was an {@link OutOfMemoryError}. */ 69 public static final int TYPE_OUT_OF_MEMORY = 4; 70 71 /** The {@link Type} of the playback failure. */ 72 @Type public final int type; 73 74 /** If {@link #type} is {@link #TYPE_RENDERER}, this is the name of the renderer. */ 75 @Nullable public final String rendererName; 76 77 /** If {@link #type} is {@link #TYPE_RENDERER}, this is the index of the renderer. */ 78 public final int rendererIndex; 79 80 /** 81 * If {@link #type} is {@link #TYPE_RENDERER}, this is the {@link Format} the renderer was using 82 * at the time of the exception, or null if the renderer wasn't using a {@link Format}. 83 */ 84 @Nullable public final Format rendererFormat; 85 86 /** 87 * If {@link #type} is {@link #TYPE_RENDERER}, this is the level of {@link FormatSupport} of the 88 * renderer for {@link #rendererFormat}. If {@link #rendererFormat} is null, this is {@link 89 * RendererCapabilities#FORMAT_HANDLED}. 90 */ 91 @FormatSupport public final int rendererFormatSupport; 92 93 /** The value of {@link SystemClock#elapsedRealtime()} when this exception was created. */ 94 public final long timestampMs; 95 96 @Nullable private final Throwable cause; 97 98 /** 99 * Creates an instance of type {@link #TYPE_SOURCE}. 100 * 101 * @param cause The cause of the failure. 102 * @return The created instance. 103 */ createForSource(IOException cause)104 public static ExoPlaybackException createForSource(IOException cause) { 105 return new ExoPlaybackException(TYPE_SOURCE, cause); 106 } 107 108 /** 109 * Creates an instance of type {@link #TYPE_RENDERER}. 110 * 111 * @param cause The cause of the failure. 112 * @param rendererIndex The index of the renderer in which the failure occurred. 113 * @param rendererFormat The {@link Format} the renderer was using at the time of the exception, 114 * or null if the renderer wasn't using a {@link Format}. 115 * @param rendererFormatSupport The {@link FormatSupport} of the renderer for {@code 116 * rendererFormat}. Ignored if {@code rendererFormat} is null. 117 * @return The created instance. 118 */ createForRenderer( Exception cause, String rendererName, int rendererIndex, @Nullable Format rendererFormat, @FormatSupport int rendererFormatSupport)119 public static ExoPlaybackException createForRenderer( 120 Exception cause, 121 String rendererName, 122 int rendererIndex, 123 @Nullable Format rendererFormat, 124 @FormatSupport int rendererFormatSupport) { 125 return new ExoPlaybackException( 126 TYPE_RENDERER, 127 cause, 128 /* customMessage= */ null, 129 rendererName, 130 rendererIndex, 131 rendererFormat, 132 rendererFormat == null ? RendererCapabilities.FORMAT_HANDLED : rendererFormatSupport); 133 } 134 135 /** 136 * Creates an instance of type {@link #TYPE_UNEXPECTED}. 137 * 138 * @param cause The cause of the failure. 139 * @return The created instance. 140 */ createForUnexpected(RuntimeException cause)141 public static ExoPlaybackException createForUnexpected(RuntimeException cause) { 142 return new ExoPlaybackException(TYPE_UNEXPECTED, cause); 143 } 144 145 /** 146 * Creates an instance of type {@link #TYPE_REMOTE}. 147 * 148 * @param message The message associated with the error. 149 * @return The created instance. 150 */ createForRemote(String message)151 public static ExoPlaybackException createForRemote(String message) { 152 return new ExoPlaybackException(TYPE_REMOTE, message); 153 } 154 155 /** 156 * Creates an instance of type {@link #TYPE_OUT_OF_MEMORY}. 157 * 158 * @param cause The cause of the failure. 159 * @return The created instance. 160 */ createForOutOfMemoryError(OutOfMemoryError cause)161 public static ExoPlaybackException createForOutOfMemoryError(OutOfMemoryError cause) { 162 return new ExoPlaybackException(TYPE_OUT_OF_MEMORY, cause); 163 } 164 ExoPlaybackException(@ype int type, Throwable cause)165 private ExoPlaybackException(@Type int type, Throwable cause) { 166 this( 167 type, 168 cause, 169 /* customMessage= */ null, 170 /* rendererName= */ null, 171 /* rendererIndex= */ C.INDEX_UNSET, 172 /* rendererFormat= */ null, 173 /* rendererFormatSupport= */ RendererCapabilities.FORMAT_HANDLED); 174 } 175 ExoPlaybackException(@ype int type, String message)176 private ExoPlaybackException(@Type int type, String message) { 177 this( 178 type, 179 /* cause= */ null, 180 /* customMessage= */ message, 181 /* rendererName= */ null, 182 /* rendererIndex= */ C.INDEX_UNSET, 183 /* rendererFormat= */ null, 184 /* rendererFormatSupport= */ RendererCapabilities.FORMAT_HANDLED); 185 } 186 ExoPlaybackException( @ype int type, @Nullable Throwable cause, @Nullable String customMessage, @Nullable String rendererName, int rendererIndex, @Nullable Format rendererFormat, @FormatSupport int rendererFormatSupport)187 private ExoPlaybackException( 188 @Type int type, 189 @Nullable Throwable cause, 190 @Nullable String customMessage, 191 @Nullable String rendererName, 192 int rendererIndex, 193 @Nullable Format rendererFormat, 194 @FormatSupport int rendererFormatSupport) { 195 super( 196 deriveMessage( 197 type, 198 customMessage, 199 rendererName, 200 rendererIndex, 201 rendererFormat, 202 rendererFormatSupport), 203 cause); 204 this.type = type; 205 this.cause = cause; 206 this.rendererName = rendererName; 207 this.rendererIndex = rendererIndex; 208 this.rendererFormat = rendererFormat; 209 this.rendererFormatSupport = rendererFormatSupport; 210 timestampMs = SystemClock.elapsedRealtime(); 211 } 212 213 /** 214 * Retrieves the underlying error when {@link #type} is {@link #TYPE_SOURCE}. 215 * 216 * @throws IllegalStateException If {@link #type} is not {@link #TYPE_SOURCE}. 217 */ getSourceException()218 public IOException getSourceException() { 219 Assertions.checkState(type == TYPE_SOURCE); 220 return (IOException) Assertions.checkNotNull(cause); 221 } 222 223 /** 224 * Retrieves the underlying error when {@link #type} is {@link #TYPE_RENDERER}. 225 * 226 * @throws IllegalStateException If {@link #type} is not {@link #TYPE_RENDERER}. 227 */ getRendererException()228 public Exception getRendererException() { 229 Assertions.checkState(type == TYPE_RENDERER); 230 return (Exception) Assertions.checkNotNull(cause); 231 } 232 233 /** 234 * Retrieves the underlying error when {@link #type} is {@link #TYPE_UNEXPECTED}. 235 * 236 * @throws IllegalStateException If {@link #type} is not {@link #TYPE_UNEXPECTED}. 237 */ getUnexpectedException()238 public RuntimeException getUnexpectedException() { 239 Assertions.checkState(type == TYPE_UNEXPECTED); 240 return (RuntimeException) Assertions.checkNotNull(cause); 241 } 242 243 /** 244 * Retrieves the underlying error when {@link #type} is {@link #TYPE_OUT_OF_MEMORY}. 245 * 246 * @throws IllegalStateException If {@link #type} is not {@link #TYPE_OUT_OF_MEMORY}. 247 */ getOutOfMemoryError()248 public OutOfMemoryError getOutOfMemoryError() { 249 Assertions.checkState(type == TYPE_OUT_OF_MEMORY); 250 return (OutOfMemoryError) Assertions.checkNotNull(cause); 251 } 252 253 @Nullable deriveMessage( @ype int type, @Nullable String customMessage, @Nullable String rendererName, int rendererIndex, @Nullable Format rendererFormat, @FormatSupport int rendererFormatSupport)254 private static String deriveMessage( 255 @Type int type, 256 @Nullable String customMessage, 257 @Nullable String rendererName, 258 int rendererIndex, 259 @Nullable Format rendererFormat, 260 @FormatSupport int rendererFormatSupport) { 261 @Nullable String message; 262 switch (type) { 263 case TYPE_SOURCE: 264 message = "Source error"; 265 break; 266 case TYPE_RENDERER: 267 message = 268 rendererName 269 + " error" 270 + ", index=" 271 + rendererIndex 272 + ", format=" 273 + rendererFormat 274 + ", format_supported=" 275 + RendererCapabilities.getFormatSupportString(rendererFormatSupport); 276 break; 277 case TYPE_REMOTE: 278 message = "Remote error"; 279 break; 280 case TYPE_OUT_OF_MEMORY: 281 message = "Out of memory error"; 282 break; 283 case TYPE_UNEXPECTED: 284 default: 285 message = "Unexpected runtime error"; 286 break; 287 } 288 if (!TextUtils.isEmpty(customMessage)) { 289 message += ": " + customMessage; 290 } 291 return message; 292 } 293 } 294