/* * Copyright (C) 2014 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 com.google.android.exoplayer.util; import com.google.android.exoplayer.ParserException; import android.net.Uri; import android.os.AsyncTask; import java.io.IOException; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.URL; /** * An {@link AsyncTask} for loading and parsing media manifests. * * @param <T> The type of the manifest being parsed. */ public abstract class ManifestFetcher<T> extends AsyncTask<String, Void, T> { /** * Invoked with the result of a manifest fetch. * * @param <T> The type of the manifest being parsed. */ public interface ManifestCallback<T> { /** * Invoked from {@link #onPostExecute(Object)} with the parsed manifest. * * @param contentId The content id of the media. * @param manifest The parsed manifest. */ void onManifest(String contentId, T manifest); /** * Invoked from {@link #onPostExecute(Object)} if an error occurred. * * @param contentId The content id of the media. * @param e The error. */ void onManifestError(String contentId, Exception e); } public static final int DEFAULT_HTTP_TIMEOUT_MILLIS = 8000; private final ManifestCallback<T> callback; private final int timeoutMillis; private volatile String contentId; private volatile Exception exception; /** * @param callback The callback to provide with the parsed manifest (or error). */ public ManifestFetcher(ManifestCallback<T> callback) { this(callback, DEFAULT_HTTP_TIMEOUT_MILLIS); } /** * @param callback The callback to provide with the parsed manifest (or error). * @param timeoutMillis The timeout in milliseconds for the connection used to load the data. */ public ManifestFetcher(ManifestCallback<T> callback, int timeoutMillis) { this.callback = callback; this.timeoutMillis = timeoutMillis; } @Override protected final T doInBackground(String... data) { try { contentId = data.length > 1 ? data[1] : null; String urlString = data[0]; String inputEncoding = null; InputStream inputStream = null; try { Uri baseUrl = Util.parseBaseUri(urlString); HttpURLConnection connection = configureHttpConnection(new URL(urlString)); inputStream = connection.getInputStream(); inputEncoding = connection.getContentEncoding(); return parse(inputStream, inputEncoding, contentId, baseUrl); } finally { if (inputStream != null) { inputStream.close(); } } } catch (Exception e) { exception = e; return null; } } @Override protected final void onPostExecute(T manifest) { if (exception != null) { callback.onManifestError(contentId, exception); } else { callback.onManifest(contentId, manifest); } } /** * Reads the {@link InputStream} and parses it into a manifest. Invoked from the * {@link AsyncTask}'s background thread. * * @param stream The input stream to read. * @param inputEncoding The encoding of the input stream. * @param contentId The content id of the media. * @param baseUrl Required where the manifest contains urls that are relative to a base url. May * be null where this is not the case. * @throws IOException If an error occurred loading the data. * @throws ParserException If an error occurred parsing the loaded data. */ protected abstract T parse(InputStream stream, String inputEncoding, String contentId, Uri baseUrl) throws IOException, ParserException; private HttpURLConnection configureHttpConnection(URL url) throws IOException { HttpURLConnection connection = (HttpURLConnection) url.openConnection(); connection.setConnectTimeout(timeoutMillis); connection.setReadTimeout(timeoutMillis); connection.setDoOutput(false); connection.connect(); return connection; } }